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(always)]
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(always)]
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 {
377 numerator.shs(F) / denominator
378 };
379 Self::new(inner)
380 }
381}
382
383impl<T: Accu<A>, A: Shift + Mul<Output = A>, const F: i8> Q<T, A, F> {
384 #[inline]
391 #[must_use]
392 pub fn apply(self, rhs: T) -> T {
393 self.mul_wide(rhs).quantize()
394 }
395}
396
397impl<T: Accu<A> + Shift, A, const F: i8> From<(T, i8)> for Q<T, A, F> {
404 fn from(value: (T, i8)) -> Self {
405 Self::new(value.0.shs(F - value.1))
406 }
407}
408
409impl<T, A, const F: i8> From<Q<T, A, F>> for (T, i8) {
417 fn from(value: Q<T, A, F>) -> Self {
418 (value.inner, F)
419 }
420}
421
422impl<T, A, const F: i8> Q<T, A, F>
423where
424 f32: AsPrimitive<Q<T, A, F>>,
425 Self: Copy + 'static,
426{
427 #[inline]
429 pub fn from_f32(value: f32) -> Self {
430 value.as_()
431 }
432}
433
434impl<T, A, const F: i8> Q<T, A, F>
435where
436 f64: AsPrimitive<Q<T, A, F>>,
437 Self: Copy + 'static,
438{
439 #[inline]
441 pub fn from_f64(value: f64) -> Self {
442 value.as_()
443 }
444}
445
446#[allow(private_bounds)]
447impl<T: AsFloat, A, const F: i8> Q<T, A, F> {
448 #[inline]
450 #[must_use]
451 pub fn as_f32(self) -> f32 {
452 self.inner.as_f32() * Self::DELTA
453 }
454
455 #[inline]
457 #[must_use]
458 pub fn as_f64(self) -> f64 {
459 self.inner.as_f64() * Self::DELTA as f64
460 }
461}
462
463macro_rules! impl_q {
464 ($alias:ident<$t:ty, $a:ty>) => {
466 impl_q!($alias<$t, $a>, $t, |x| x as _, core::convert::identity);
467 };
468 ($alias:ident<$t:ty, $a:ty>, $wrap:tt) => {
470 impl_q!($alias<$wrap<$t>, $wrap<$a>>, $t, |x: $wrap<_>| $wrap(x.0 as _), $wrap);
471 };
472 ($alias:ident<$t:ty, $a:ty>, $inner:ty, $as:expr, $wrap:expr) => {
474 impl Accu<$a> for $t {
475 #[inline(always)]
476 fn up(self) -> $a {
477 $as(self)
478 }
479 #[inline(always)]
480 fn down(a: $a) -> Self {
481 $as(a)
482 }
483 }
484
485 #[doc = concat!("Fixed point [`", stringify!($t), "`] with [`", stringify!($a), "`] accumulator")]
486 pub type $alias<const F: i8> = Q<$t, $a, F>;
487
488 impl<const F: i8> ConstOne for Q<$t, $a, F> {
489 const ONE: Self = {
490 const {
491 const MAX_ONE_F: i8 =
492 <$inner>::BITS as i8 - if <$inner>::MIN == 0 { 0 } else { 1 };
493 assert!(
494 F >= 0 && F < MAX_ONE_F,
495 "`Q::ONE` is only available when 1 is exactly representable"
496 );
497 }
498 Self::new($wrap(1 << F as usize))
499 };
500 }
501
502 impl<const F: i8> One for Q<$t, $a, F> {
503 fn one() -> Self {
504 const {
505 const MAX_ONE_F: i8 =
506 <$inner>::BITS as i8 - if <$inner>::MIN == 0 { 0 } else { 1 };
507 assert!(
508 F >= 0 && F < MAX_ONE_F,
509 "`Q::one()` is only available when 1 is exactly representable"
510 );
511 }
512 Self::ONE
513 }
514 }
515
516 impl<const F: i8> Mul<Q<$t, $a, F>> for $t {
518 type Output = $t;
519
520 #[inline]
521 fn mul(self, rhs: Q<$t, $a, F>) -> Self::Output {
522 rhs.apply(self)
523 }
524 }
525
526 impl<const F: i8> Div<Q<$t, $a, F>> for $t {
528 type Output = $t;
529
530 #[inline]
531 fn div(self, rhs: Q<$t, $a, F>) -> Self::Output {
532 if F > 0 {
533 <$t>::down(self.up().shs(F) / rhs.inner.up())
534 } else {
535 self.shsc::<F>() / rhs.inner
536 }
537 }
538 }
539 };
540}
541impl_q!(Q8<i8, i16>);
543impl_q!(Q16<i16, i32>);
544impl_q!(Q32<i32, i64>);
545impl_q!(Q64<i64, i128>);
546impl_q!(P8<u8, u16>);
548impl_q!(P16<u16, u32>);
549impl_q!(P32<u32, u64>);
550impl_q!(P64<u64, u128>);
551impl_q!(W8<i8, i16>, Wrapping);
553impl_q!(W16<i16, i32>, Wrapping);
554impl_q!(W32<i32, i64>, Wrapping);
555impl_q!(W64<i64, i128>, Wrapping);
556impl_q!(V8<u8, u16>, Wrapping);
558impl_q!(V16<u16, u32>, Wrapping);
559impl_q!(V32<u32, u64>, Wrapping);
560impl_q!(V64<u64, u128>, Wrapping);
561
562#[cfg(test)]
565mod test {
566 use super::*;
567 use num_traits::{Bounded, FromPrimitive, Signed, ToPrimitive};
568
569 #[test]
570 fn ratio() {
571 let third = Q32::<28>::from_ratio(1i32, 3);
572 assert!((third.as_f64() - 1.0 / 3.0).abs() < Q32::<28>::DELTA as f64);
573 assert_eq!(Q32::<8>::from_ratio(-3, 2), Q32::from_bits(-384));
574
575 let reciprocal = 3.0f32.recip();
576 assert_eq!(
577 f32::from_ratios([1.0, 2.0], 3.0),
578 [reciprocal, 2.0 * reciprocal]
579 );
580 assert_eq!(
581 Q32::<8>::from_ratios([-3, 3], 2),
582 [Q32::from_bits(-384), Q32::from_bits(384)]
583 );
584 }
585
586 #[test]
587 fn simple() {
588 assert_eq!(
589 Q32::<5>::from_int(4) * Q32::<5>::from_int(3),
590 Q32::from_int(3 * 4)
591 );
592 assert_eq!(
593 Q32::<5>::from_int(12) / Q32::<5>::from_int(6),
594 Q32::from_int(2)
595 );
596 assert_eq!(7 * Q32::<4>::from_bits(0x33), 7 * 3 + ((3 * 7) >> 4));
597 assert_eq!(Q32::<4>::from_bits(0x33).apply(7), 7 * 3 + ((3 * 7) >> 4));
598 assert_eq!(
599 Q32::<4>::from_bits(0x33).mul_wide(7).quantize(),
600 7 * Q32::<4>::from_bits(0x33)
601 );
602 }
603
604 #[test]
605 fn numeric_traits() {
606 assert_eq!(Q8::<4>::min_value().into_bits(), i8::MIN);
607 assert_eq!(Q8::<4>::max_value().into_bits(), i8::MAX);
608 assert_eq!(Q8::<4>::from_f32(1.5).to_i32(), Some(1));
609 assert_eq!(Q8::<4>::from_f32(1.5).to_f64(), Some(1.5));
610 assert_eq!(Q8::<4>::from_i32(3), Some(Q8::<4>::from_int(3)));
611 assert_eq!(Q8::<4>::from_f32(-1.5).abs(), Q8::<4>::from_f32(1.5));
612 assert_eq!(Q8::<4>::from_f32(-1.5).signum(), Q8::<4>::from_int(-1));
613 assert!(Q8::<4>::from_f32(-1.5).is_negative());
614 }
615
616 #[cfg(feature = "bytemuck")]
617 #[test]
618 fn bytemuck_traits() {
619 use bytemuck::TransparentWrapper;
620
621 let q = Q8::<4>::from_int(3);
622 assert_eq!(q.into_bits(), 48);
623 assert_eq!(Q8::<4>::wrap(48i8), q);
624 assert_eq!(*Q8::<4>::wrap_ref(&48i8), q);
625 }
626}