1#![cfg_attr(not(feature = "std"), no_std)]
2#![doc = include_str!("../README.md")]
3
4mod format;
5mod num_traits_impl;
6mod ops;
7#[cfg(feature = "serde")]
8pub mod serde;
9
10use num_traits::{AsPrimitive, ConstOne, One};
11
12use core::{
13 hash::{Hash, Hasher},
14 marker::PhantomData,
15 num::Wrapping,
16 ops::{Div, Mul, Shl, Shr},
17};
18
19pub trait FromRatio<T> {
25 fn from_ratio(numerator: T, denominator: T) -> Self;
27
28 fn from_ratios<const N: usize>(numerators: [T; N], denominator: T) -> [Self; N]
33 where
34 T: Copy,
35 Self: Sized,
36 {
37 numerators.map(|numerator| Self::from_ratio(numerator, denominator))
38 }
39}
40
41impl FromRatio<f32> for f32 {
42 #[inline]
43 fn from_ratio(numerator: f32, denominator: f32) -> Self {
44 numerator / denominator
45 }
46
47 #[inline]
48 fn from_ratios<const N: usize>(numerators: [f32; N], denominator: f32) -> [Self; N] {
49 let reciprocal = denominator.recip();
50 numerators.map(|numerator| numerator * reciprocal)
51 }
52}
53
54impl FromRatio<f64> for f64 {
55 #[inline]
56 fn from_ratio(numerator: f64, denominator: f64) -> Self {
57 numerator / denominator
58 }
59
60 #[inline]
61 fn from_ratios<const N: usize>(numerators: [f64; N], denominator: f64) -> [Self; N] {
62 let reciprocal = denominator.recip();
63 numerators.map(|numerator| numerator * reciprocal)
64 }
65}
66
67pub(crate) trait AsFloat: Copy {
69 fn as_f32(self) -> f32;
70 fn as_f64(self) -> f64;
71}
72
73macro_rules! impl_as_float {
74 ($($ty:ty),* $(,)?) => {
75 $(
76 impl AsFloat for $ty {
77 #[inline]
78 fn as_f32(self) -> f32 {
79 self as f32
80 }
81
82 #[inline]
83 fn as_f64(self) -> f64 {
84 self as f64
85 }
86 }
87
88 impl AsFloat for Wrapping<$ty> {
89 #[inline]
90 fn as_f32(self) -> f32 {
91 self.0 as f32
92 }
93
94 #[inline]
95 fn as_f64(self) -> f64 {
96 self.0 as f64
97 }
98 }
99 )*
100 };
101}
102
103impl_as_float!(i8, i16, i32, i64, u8, u16, u32, u64);
104
105pub trait Shift: Copy + Shl<usize, Output = Self> + Shr<usize, Output = Self> {
109 fn shs(self, f: i8) -> Self;
119
120 #[inline]
122 fn shsc<const F: i8>(self) -> Self {
123 const { assert!(F > i8::MIN, "shift must not be i8::MIN") }
124 self.shs(F)
125 }
126}
127
128impl<T: Copy + Shl<usize, Output = T> + Shr<usize, Output = T>> Shift for T {
129 #[inline]
130 fn shs(self, f: i8) -> Self {
131 debug_assert!(f > i8::MIN, "shift must not be i8::MIN");
132 if f >= 0 {
133 self << (f as _)
134 } else {
135 self >> (-f as _)
136 }
137 }
138}
139
140pub trait Accu<A> {
142 fn up(self) -> A;
151
152 fn down(a: A) -> Self;
161
162 }
171
172#[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
194#[derive(Default)]
195#[repr(transparent)]
196#[cfg_attr(feature = "serde", serde(transparent))]
197#[cfg_attr(
198 feature = "bytemuck",
199 derive(bytemuck::Pod, bytemuck::TransparentWrapper, bytemuck::Zeroable),
200 transparent(T)
201)]
202#[must_use]
203pub struct Q<T, A, const F: i8> {
204 _accu: PhantomData<A>,
206 inner: T,
208}
209
210impl<T: Clone, A, const F: i8> Clone for Q<T, A, F> {
211 #[inline]
212 fn clone(&self) -> Self {
213 Self {
214 _accu: PhantomData,
215 inner: self.inner.clone(),
216 }
217 }
218}
219
220impl<T: Copy, A, const F: i8> Copy for Q<T, A, F> {}
221
222impl<T: PartialEq, A, const F: i8> PartialEq for Q<T, A, F> {
223 #[inline]
224 fn eq(&self, other: &Self) -> bool {
225 self.inner.eq(&other.inner)
226 }
227}
228
229impl<T: Eq, A, const F: i8> Eq for Q<T, A, F> where Self: PartialEq {}
230
231impl<T: Hash, A, const F: i8> Hash for Q<T, A, F> {
232 #[inline]
233 fn hash<H: Hasher>(&self, state: &mut H) {
234 self.inner.hash(state)
235 }
236}
237
238impl<T: PartialOrd, A, const F: i8> PartialOrd for Q<T, A, F> {
239 #[inline]
240 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
241 self.inner.partial_cmp(&other.inner)
242 }
243}
244
245impl<T: Ord, A, const F: i8> Ord for Q<T, A, F>
246where
247 Self: PartialOrd,
248{
249 #[inline]
250 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
251 self.inner.cmp(&other.inner)
252 }
253}
254
255impl<T, A, const F: i8> Q<T, A, F> {
256 pub const DELTA: f32 = if F > 0 {
269 1.0 / (1u128 << F) as f32
270 } else {
271 (1u128 << -F) as f32
272 };
273
274 #[inline]
275 const fn new(inner: T) -> Self {
276 Self {
277 _accu: PhantomData,
278 inner,
279 }
280 }
281
282 #[inline]
284 pub const fn from_bits(bits: T) -> Self {
285 Self::new(bits)
286 }
287
288 #[inline]
290 #[must_use]
291 pub fn into_bits(self) -> T {
292 self.inner
293 }
294}
295
296impl<T: Shift, A, const F: i8> Q<T, A, F> {
297 #[inline]
306 pub fn scale<const F1: i8>(self) -> Q<T, A, F1> {
307 Q::new(self.inner.shs(const { F1 - F }))
308 }
309
310 #[inline]
317 #[must_use]
318 pub fn trunc(self) -> T {
319 self.inner.shs(const { -F })
320 }
321
322 #[inline]
329 pub fn from_int(value: T) -> Self {
330 Self::new(value.shsc::<F>())
331 }
332}
333
334impl<A: Shift, T: Accu<A>, const F: i8> Q<A, T, F> {
335 #[inline]
344 #[must_use]
345 pub fn quantize(self) -> T {
346 T::down(self.trunc())
347 }
348}
349
350impl<T: Accu<A>, A: Mul<Output = A>, const F: i8> Q<T, A, F> {
351 #[inline]
361 pub fn mul_wide(self, rhs: T) -> Q<A, T, F> {
362 Q::new(self.inner.up() * rhs.up())
363 }
364}
365
366impl<T, A, const F: i8> FromRatio<T> for Q<T, A, F>
367where
368 T: Copy + Shift + Accu<A> + Div<Output = T>,
369 A: Shift + Div<Output = A>,
370{
371 #[inline]
372 fn from_ratio(numerator: T, denominator: T) -> Self {
373 const { assert!(F > i8::MIN, "fractional bits must not be i8::MIN") }
374 let inner = if F > 0 {
375 T::down(numerator.up().shs(F) / denominator.up())
376 } else if F == 0 {
377 numerator / denominator
378 } else {
379 T::down(numerator.up() / denominator.up().shs(-F))
380 };
381 Self::new(inner)
382 }
383}
384
385impl<T: Accu<A>, A: Shift + Mul<Output = A>, const F: i8> Q<T, A, F> {
386 #[inline]
393 #[must_use]
394 pub fn apply(self, rhs: T) -> T {
395 self.mul_wide(rhs).quantize()
396 }
397}
398
399impl<T: Accu<A> + Shift, A, const F: i8> From<(T, i8)> for Q<T, A, F> {
406 fn from(value: (T, i8)) -> Self {
407 Self::new(value.0.shs(F - value.1))
408 }
409}
410
411impl<T, A, const F: i8> From<Q<T, A, F>> for (T, i8) {
419 fn from(value: Q<T, A, F>) -> Self {
420 (value.inner, F)
421 }
422}
423
424impl<T, A, const F: i8> Q<T, A, F>
425where
426 f32: AsPrimitive<Q<T, A, F>>,
427 Self: Copy + 'static,
428{
429 #[inline]
431 pub fn from_f32(value: f32) -> Self {
432 value.as_()
433 }
434}
435
436impl<T, A, const F: i8> Q<T, A, F>
437where
438 f64: AsPrimitive<Q<T, A, F>>,
439 Self: Copy + 'static,
440{
441 #[inline]
443 pub fn from_f64(value: f64) -> Self {
444 value.as_()
445 }
446}
447
448#[allow(private_bounds)]
449impl<T: AsFloat, A, const F: i8> Q<T, A, F> {
450 #[inline]
452 #[must_use]
453 pub fn as_f32(self) -> f32 {
454 self.inner.as_f32() * Self::DELTA
455 }
456
457 #[inline]
459 #[must_use]
460 pub fn as_f64(self) -> f64 {
461 self.inner.as_f64() * Self::DELTA as f64
462 }
463}
464
465macro_rules! impl_q {
466 ($alias:ident<$t:ty, $a:ty>) => {
468 impl_q!($alias<$t, $a>, $t, |x| x as _, core::convert::identity);
469 };
470 ($alias:ident<$t:ty, $a:ty>, $wrap:tt) => {
472 impl_q!($alias<$wrap<$t>, $wrap<$a>>, $t, |x: $wrap<_>| $wrap(x.0 as _), $wrap);
473 };
474 ($alias:ident<$t:ty, $a:ty>, $inner:ty, $as:expr, $wrap:expr) => {
476 impl Accu<$a> for $t {
477 #[inline]
478 fn up(self) -> $a {
479 $as(self)
480 }
481 #[inline]
482 fn down(a: $a) -> Self {
483 $as(a)
484 }
485 }
486
487 #[doc = concat!("Fixed point [`", stringify!($t), "`] with [`", stringify!($a), "`] accumulator")]
488 pub type $alias<const F: i8> = Q<$t, $a, F>;
489
490 impl<const F: i8> ConstOne for Q<$t, $a, F> {
491 const ONE: Self = {
492 const {
493 const MAX_ONE_F: i8 =
494 <$inner>::BITS as i8 - if <$inner>::MIN == 0 { 0 } else { 1 };
495 assert!(
496 F >= 0 && F < MAX_ONE_F,
497 "`Q::ONE` is only available when 1 is exactly representable"
498 );
499 }
500 Self::new($wrap(1 << F as usize))
501 };
502 }
503
504 impl<const F: i8> One for Q<$t, $a, F> {
505 fn one() -> Self {
506 const {
507 const MAX_ONE_F: i8 =
508 <$inner>::BITS as i8 - if <$inner>::MIN == 0 { 0 } else { 1 };
509 assert!(
510 F >= 0 && F < MAX_ONE_F,
511 "`Q::one()` is only available when 1 is exactly representable"
512 );
513 }
514 Self::ONE
515 }
516 }
517
518 impl<const F: i8> Mul<Q<$t, $a, F>> for $t {
520 type Output = $t;
521
522 #[inline]
523 fn mul(self, rhs: Q<$t, $a, F>) -> Self::Output {
524 rhs.apply(self)
525 }
526 }
527
528 impl<const F: i8> Div<Q<$t, $a, F>> for $t {
530 type Output = $t;
531
532 #[inline]
533 fn div(self, rhs: Q<$t, $a, F>) -> Self::Output {
534 Q::<$t, $a, F>::from_ratio(self, rhs.inner).inner
535 }
536 }
537 };
538}
539impl_q!(Q8<i8, i16>);
541impl_q!(Q16<i16, i32>);
542impl_q!(Q32<i32, i64>);
543impl_q!(Q64<i64, i128>);
544impl_q!(P8<u8, u16>);
546impl_q!(P16<u16, u32>);
547impl_q!(P32<u32, u64>);
548impl_q!(P64<u64, u128>);
549impl_q!(W8<i8, i16>, Wrapping);
551impl_q!(W16<i16, i32>, Wrapping);
552impl_q!(W32<i32, i64>, Wrapping);
553impl_q!(W64<i64, i128>, Wrapping);
554impl_q!(V8<u8, u16>, Wrapping);
556impl_q!(V16<u16, u32>, Wrapping);
557impl_q!(V32<u32, u64>, Wrapping);
558impl_q!(V64<u64, u128>, Wrapping);
559
560#[cfg(test)]
563mod test {
564 use super::*;
565 use num_traits::{Bounded, FromPrimitive, Signed, ToPrimitive};
566
567 #[test]
568 fn ratio() {
569 let third = Q32::<28>::from_ratio(1i32, 3);
570 assert!((third.as_f64() - 1.0 / 3.0).abs() < Q32::<28>::DELTA as f64);
571 assert_eq!(Q32::<8>::from_ratio(-3, 2), Q32::from_bits(-384));
572
573 let reciprocal = 3.0f32.recip();
574 assert_eq!(
575 f32::from_ratios([1.0, 2.0], 3.0),
576 [reciprocal, 2.0 * reciprocal]
577 );
578 assert_eq!(
579 Q32::<8>::from_ratios([-3, 3], 2),
580 [Q32::from_bits(-384), Q32::from_bits(384)]
581 );
582 }
583
584 #[test]
585 fn simple() {
586 assert_eq!(
587 Q32::<5>::from_int(4) * Q32::<5>::from_int(3),
588 Q32::from_int(3 * 4)
589 );
590 assert_eq!(
591 Q32::<5>::from_int(12) / Q32::<5>::from_int(6),
592 Q32::from_int(2)
593 );
594 assert_eq!(7 * Q32::<4>::from_bits(0x33), 7 * 3 + ((3 * 7) >> 4));
595 assert_eq!(Q32::<4>::from_bits(0x33).apply(7), 7 * 3 + ((3 * 7) >> 4));
596 assert_eq!(
597 Q32::<4>::from_bits(0x33).mul_wide(7).quantize(),
598 7 * Q32::<4>::from_bits(0x33)
599 );
600 }
601
602 #[test]
603 fn numeric_traits() {
604 assert_eq!(Q8::<4>::min_value().into_bits(), i8::MIN);
605 assert_eq!(Q8::<4>::max_value().into_bits(), i8::MAX);
606 assert_eq!(Q8::<4>::from_f32(1.5).to_i32(), Some(1));
607 assert_eq!(Q8::<4>::from_f32(1.5).to_f64(), Some(1.5));
608 assert_eq!(Q8::<4>::from_i32(3), Some(Q8::<4>::from_int(3)));
609 assert_eq!(Q8::<4>::from_f32(-1.5).abs(), Q8::<4>::from_f32(1.5));
610 assert_eq!(Q8::<4>::from_f32(-1.5).signum(), Q8::<4>::from_int(-1));
611 assert!(Q8::<4>::from_f32(-1.5).is_negative());
612 }
613
614 #[cfg(feature = "bytemuck")]
615 #[test]
616 fn bytemuck_traits() {
617 use bytemuck::TransparentWrapper;
618
619 let q = Q8::<4>::from_int(3);
620 assert_eq!(q.into_bits(), 48);
621 assert_eq!(Q8::<4>::wrap(48i8), q);
622 assert_eq!(*Q8::<4>::wrap_ref(&48i8), q);
623 }
624}