Skip to main content

dsp_fixedpoint/
lib.rs

1#![cfg_attr(not(feature = "std"), no_std)]
2#![doc = include_str!("../README.md")]
3
4#[cfg(not(feature = "std"))]
5#[allow(unused_imports)]
6use num_traits::float::FloatCore;
7use num_traits::{AsPrimitive, ConstOne, ConstZero, One, Zero};
8
9use core::{
10    fmt, iter,
11    marker::PhantomData,
12    num::Wrapping,
13    ops::{Add, Div, Mul, Neg, Rem, Shl, Shr, Sub},
14    ops::{AddAssign, DivAssign, MulAssign, RemAssign, ShlAssign, ShrAssign, SubAssign},
15    ops::{BitAnd, BitOr, BitXor, Not},
16    ops::{BitAndAssign, BitOrAssign, BitXorAssign},
17};
18
19/// Shift summary trait
20///
21/// Wrapping supports `Sh{lr}<usize>` only.
22pub trait Shift: Copy + Shl<usize, Output = Self> + Shr<usize, Output = Self> {
23    /// Signed shift (positive: left)
24    ///
25    /// `x*2**f`
26    ///
27    /// ```
28    /// # use dsp_fixedpoint::Shift;
29    /// assert_eq!(1i32.shs(1), 2);
30    /// assert_eq!(4i32.shs(-1), 2);
31    /// ```
32    fn shs(self, f: i8) -> Self;
33}
34
35impl<T: Copy + Shl<usize, Output = T> + Shr<usize, Output = T>> Shift for T {
36    #[inline(always)]
37    fn shs(self, f: i8) -> Self {
38        if f >= 0 {
39            self << (f as _)
40        } else {
41            self >> (-f as _)
42        }
43    }
44}
45
46/// Conversion trait between base and accumulator type
47pub trait Accu<A> {
48    /// Cast up to accumulator type
49    ///
50    /// This is a primitive cast.
51    ///
52    /// ```
53    /// # use dsp_fixedpoint::Accu;
54    /// assert_eq!(3i32.up(), 3i64);
55    /// ```
56    fn up(self) -> A;
57
58    /// Cast down from accumulator type
59    ///
60    /// This is a primitive cast.
61    ///
62    /// ```
63    /// # use dsp_fixedpoint::Accu;
64    /// assert_eq!(i16::down(3i32), 3i16);
65    /// ```
66    fn down(a: A) -> Self;
67
68    // /// Cast to f32
69    // fn as_f32(self) -> f32;
70    // /// Cast to f64
71    // fn as_f64(self) -> f64;
72    // /// Cast from f32
73    // fn f32_as(value: f64) -> Self;
74    // /// Cast from f64
75    // fn f64_as(value: f64) -> Self;
76}
77
78/// Fixed point integer
79///
80/// Generics:
81/// * `T`: Base integer
82/// * `A`: Accumulator for intermediate results
83/// * `F`: Number of fractional bits right of the decimal point
84///
85/// `F` negative is supported analogously.
86///
87/// * `Q32<31>` is `(-1..1).step_by(2^-31)`
88/// * `Q<i16, _, 20>` is `(-1/32..1/32).step_by(2^-20)`
89/// * `Q<u8, _, 4>` is `(0..16).step_by(1/16)`
90/// * `Q<u8, _, -2>` is `(0..1024).step_by(4)`
91///
92/// ```
93/// # use dsp_fixedpoint::Q8;
94/// assert_eq!(Q8::<4>::from_int(3), Q8::new(3 << 4));
95/// assert_eq!(7 * Q8::<4>::from_f32(1.5), 10);
96/// assert_eq!(7 / Q8::<4>::from_f32(1.5), 4);
97/// ```
98#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
99#[derive(Default)]
100#[repr(transparent)]
101#[cfg_attr(feature = "serde", serde(transparent))]
102pub struct Q<T, A, const F: i8> {
103    /// The accumulator type
104    _accu: PhantomData<A>,
105    /// The inner value representation
106    pub inner: T,
107}
108
109impl<T: Clone, A, const F: i8> Clone for Q<T, A, F> {
110    #[inline]
111    fn clone(&self) -> Self {
112        Self {
113            _accu: PhantomData,
114            inner: self.inner.clone(),
115        }
116    }
117}
118
119impl<T: Copy, A, const F: i8> Copy for Q<T, A, F> {}
120
121impl<T: PartialEq, A, const F: i8> PartialEq for Q<T, A, F> {
122    #[inline]
123    fn eq(&self, other: &Self) -> bool {
124        self.inner.eq(&other.inner)
125    }
126}
127
128impl<T: Eq, A, const F: i8> Eq for Q<T, A, F> where Self: PartialEq {}
129
130impl<T: PartialOrd, A, const F: i8> PartialOrd for Q<T, A, F> {
131    #[inline]
132    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
133        self.inner.partial_cmp(&other.inner)
134    }
135}
136
137impl<T: Ord, A, const F: i8> Ord for Q<T, A, F>
138where
139    Self: PartialOrd,
140{
141    #[inline]
142    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
143        self.inner.cmp(&other.inner)
144    }
145}
146
147impl<T: One + Shift, A, const F: i8> One for Q<T, A, F>
148where
149    Self: Mul<Output = Self>,
150{
151    fn one() -> Self {
152        Self::new(T::one().shs(F))
153    }
154}
155
156impl<T: Zero, A, const F: i8> Zero for Q<T, A, F>
157where
158    Self: Add<Output = Self>,
159{
160    fn zero() -> Self {
161        Self::new(T::zero())
162    }
163
164    fn is_zero(&self) -> bool {
165        self.inner.is_zero()
166    }
167}
168
169impl<T: ConstZero, A, const F: i8> ConstZero for Q<T, A, F> {
170    const ZERO: Self = Self::new(T::ZERO);
171}
172
173impl<T, A, const F: i8> Q<T, A, F> {
174    /// Step between distinct numbers
175    ///
176    /// ```
177    /// # use dsp_fixedpoint::Q32;
178    /// assert_eq!(Q32::<31>::DELTA, 2f32.powi(-31));
179    /// assert_eq!(Q32::<-4>::DELTA, 2f32.powi(4));
180    /// ```
181    pub const DELTA: f32 = if F > 0 {
182        1.0 / (1u128 << F) as f32
183    } else {
184        (1u128 << -F) as f32
185    };
186
187    /// Create a new fixed point number from a given representation
188    ///
189    /// ```
190    /// # use dsp_fixedpoint::P8;
191    /// assert_eq!(P8::<9>::new(3).inner, 3);
192    /// ```
193    #[inline]
194    pub const fn new(inner: T) -> Self {
195        Self {
196            _accu: PhantomData,
197            inner,
198        }
199    }
200}
201
202impl<T: Shift, A, const F: i8> Q<T, A, F> {
203    /// Convert to a different number of fractional bits (truncating)
204    ///
205    /// Use this liberally for Add/Sub/Rem with Q's of different F.
206    ///
207    /// ```
208    /// # use dsp_fixedpoint::Q8;
209    /// assert_eq!(Q8::<4>::new(32).scale::<0>(), Q8::new(2));
210    /// ```
211    #[inline]
212    pub fn scale<const F1: i8>(self) -> Q<T, A, F1> {
213        Q::new(self.inner.shs(F1 - F))
214    }
215
216    /// Return the integer part
217    ///
218    /// ```
219    /// # use dsp_fixedpoint::Q8;
220    /// assert_eq!(Q8::<4>::new(0x35).trunc(), 0x3);
221    /// ```
222    #[inline]
223    pub fn trunc(self) -> T {
224        self.inner.shs(-F)
225    }
226
227    /// Scale from integer base type
228    ///
229    /// ```
230    /// # use dsp_fixedpoint::Q8;
231    /// assert_eq!(Q8::<4>::from_int(7).inner, 7 << 4);
232    /// ```
233    #[inline]
234    pub fn from_int(value: T) -> Self {
235        Self::new(value.shs(F))
236    }
237}
238
239impl<A: Shift, T: Accu<A>, const F: i8> Q<A, T, F> {
240    /// Scale from integer accu type
241    ///
242    ///
243    /// ```
244    /// # use dsp_fixedpoint::Q8;
245    /// let q = Q8::<4>::from_f32(0.25);
246    /// assert_eq!((q * 7).quantize(), (7.0 * 0.25f32).floor() as _);
247    /// ```
248    #[inline]
249    pub fn quantize(self) -> T {
250        T::down(self.trunc())
251    }
252}
253
254/// Lossy conversion from a dynamically scaled integer
255///
256/// ```
257/// # use dsp_fixedpoint::Q8;
258/// assert_eq!(Q8::<8>::from((1, 3)).inner, 1 << 5);
259/// ```
260impl<T: Accu<A> + Shift, A, const F: i8> From<(T, i8)> for Q<T, A, F> {
261    fn from(value: (T, i8)) -> Self {
262        Self::new(value.0.shs(F - value.1))
263    }
264}
265
266/// Lossless conversion into a dynamically scaled integer
267///
268/// ```
269/// # use dsp_fixedpoint::Q8;
270/// let q: (i8, i8) = Q8::<8>::new(9).into();
271/// assert_eq!(q, (9, 8));
272/// ```
273impl<T, A, const F: i8> From<Q<T, A, F>> for (T, i8) {
274    fn from(value: Q<T, A, F>) -> Self {
275        (value.inner, F)
276    }
277}
278
279/// Lossy conversion to and from float
280///
281/// ```
282/// # use dsp_fixedpoint::Q8;
283/// assert_eq!(8 * Q8::<4>::from_f32(0.25), 2);
284/// assert_eq!(8 * Q8::<4>::from_f64(0.25), 2);
285/// assert_eq!(Q8::<4>::new(4).as_f32(), 0.25);
286/// assert_eq!(Q8::<4>::new(4).as_f64(), 0.25);
287/// ```
288macro_rules! impl_as_float {
289    ($ty:ident) => {
290        impl<T: 'static + Copy, A: 'static, const F: i8> AsPrimitive<Q<T, A, F>> for $ty
291        where
292            $ty: AsPrimitive<T>,
293        {
294            #[inline]
295            fn as_(self) -> Q<T, A, F> {
296                Q::new(
297                    (self * const { 1.0 / Q::<T, A, F>::DELTA as $ty })
298                        .round()
299                        .as_(),
300                )
301            }
302        }
303
304        impl<T: AsPrimitive<$ty>, A: 'static, const F: i8> AsPrimitive<$ty> for Q<T, A, F> {
305            #[inline]
306            fn as_(self) -> $ty {
307                self.inner.as_() * Self::DELTA as $ty
308            }
309        }
310    };
311}
312impl_as_float!(f32);
313impl_as_float!(f64);
314
315impl<T, A, const F: i8> Q<T, A, F>
316where
317    f32: AsPrimitive<Q<T, A, F>>,
318    Self: Copy + 'static,
319{
320    /// Quantize a f32
321    #[inline]
322    pub fn from_f32(value: f32) -> Self {
323        value.as_()
324    }
325}
326
327impl<T, A, const F: i8> Q<T, A, F>
328where
329    f64: AsPrimitive<Q<T, A, F>>,
330    Self: Copy + 'static,
331{
332    /// Quantize a f64
333    #[inline]
334    pub fn from_f64(value: f64) -> Self {
335        value.as_()
336    }
337}
338
339impl<T, A, const F: i8> Q<T, A, F>
340where
341    Self: 'static + Copy + AsPrimitive<f32>,
342{
343    /// Convert lossy to f32
344    #[inline]
345    pub fn as_f32(self) -> f32 {
346        self.as_()
347    }
348}
349
350impl<T, A, const F: i8> Q<T, A, F>
351where
352    Self: 'static + Copy + AsPrimitive<f64>,
353{
354    /// Convert lossy to f64
355    #[inline]
356    pub fn as_f64(self) -> f64 {
357        self.as_()
358    }
359}
360
361impl<T, A, const F: i8> AsPrimitive<Self> for Q<T, A, F>
362where
363    Self: Copy + 'static,
364{
365    fn as_(self) -> Self {
366        self
367    }
368}
369
370macro_rules! forward_unop {
371    ($tr:ident::$m:ident) => {
372        impl<T: $tr<Output = T>, A, const F: i8> $tr for Q<T, A, F> {
373            type Output = Self;
374            #[inline]
375            fn $m(self) -> Self::Output {
376                Self::new(<T as $tr>::$m(self.inner))
377            }
378        }
379    };
380}
381forward_unop!(Neg::neg);
382forward_unop!(Not::not);
383
384macro_rules! forward_sh_op {
385    ($tr:ident::$m:ident) => {
386        impl<U, T: $tr<U, Output = T>, A, const F: i8> $tr<U> for Q<T, A, F> {
387            type Output = Self;
388            #[inline]
389            fn $m(self, rhs: U) -> Self::Output {
390                Self::new(<T as $tr<U>>::$m(self.inner, rhs))
391            }
392        }
393    };
394}
395forward_sh_op!(Shr::shr);
396forward_sh_op!(Shl::shl);
397
398macro_rules! forward_sh_assign_op {
399    ($tr:ident::$m:ident) => {
400        impl<T: $tr<U>, U, A, const F: i8> $tr<U> for Q<T, A, F> {
401            #[inline]
402            fn $m(&mut self, rhs: U) {
403                <T as $tr<U>>::$m(&mut self.inner, rhs)
404            }
405        }
406    };
407}
408forward_sh_assign_op!(ShrAssign::shr_assign);
409forward_sh_assign_op!(ShlAssign::shl_assign);
410
411/// ```
412/// # use dsp_fixedpoint::Q8;
413/// assert_eq!(
414///     Q8::<3>::from_f32(3.5) + Q8::from_f32(5.2),
415///     Q8::from_f32(8.7)
416/// );
417/// assert_eq!(
418///     Q8::<3>::from_f32(4.0) - Q8::from_f32(3.2),
419///     Q8::from_f32(0.8)
420/// );
421/// assert_eq!(Q8::<3>::from_f32(3.5) % Q8::from_int(1), Q8::from_f32(0.5));
422/// ```
423macro_rules! forward_binop {
424    ($tr:ident::$m:ident) => {
425        impl<T: $tr<T, Output = T>, A, const F: i8> $tr for Q<T, A, F> {
426            type Output = Self;
427            #[inline]
428            fn $m(self, rhs: Self) -> Self::Output {
429                Self::new(<T as $tr>::$m(self.inner, rhs.inner))
430            }
431        }
432    };
433}
434forward_binop!(Rem::rem);
435forward_binop!(Add::add);
436forward_binop!(Sub::sub);
437forward_binop!(BitAnd::bitand);
438forward_binop!(BitOr::bitor);
439forward_binop!(BitXor::bitxor);
440
441// The notable exception to standard rules
442// (https://github.com/rust-lang/rust/pull/93208#issuecomment-1019310634)
443// This is for performance reasons
444//
445// Q*T -> A, Q/T -> Q
446// See also the T*Q -> T and T/Q -> T in impl_q!()
447//
448
449/// Wide multiplication to accumulator
450///
451/// ```
452/// # use dsp_fixedpoint::{Q8, Q};
453/// assert_eq!(Q8::<3>::new(4) * 2, Q::new(8));
454/// assert_eq!(Q8::<3>::new(4) / 2, Q8::new(2));
455/// ```
456impl<T: Accu<A>, A: Mul<Output = A>, const F: i8> Mul<T> for Q<T, A, F> {
457    type Output = Q<A, T, F>;
458    #[inline]
459    fn mul(self, rhs: T) -> Q<A, T, F> {
460        Q::new(self.inner.up() * rhs.up())
461    }
462}
463
464impl<T: Div<Output = T>, A, const F: i8> Div<T> for Q<T, A, F> {
465    type Output = Self;
466    #[inline]
467    fn div(self, rhs: T) -> Self {
468        Q::new(self.inner / rhs)
469    }
470}
471
472macro_rules! forward_assign_op_foreign {
473    ($tr:ident::$m:ident) => {
474        impl<T: $tr<T>, A, const F: i8> $tr<T> for Q<T, A, F> {
475            #[inline]
476            fn $m(&mut self, rhs: T) {
477                <T as $tr>::$m(&mut self.inner, rhs)
478            }
479        }
480    };
481}
482forward_assign_op_foreign!(MulAssign::mul_assign);
483forward_assign_op_foreign!(DivAssign::div_assign);
484
485macro_rules! forward_assign_op {
486    ($tr:ident::$m:ident) => {
487        impl<T: $tr<T>, A, const F: i8> $tr for Q<T, A, F> {
488            #[inline]
489            fn $m(&mut self, rhs: Self) {
490                <T as $tr>::$m(&mut self.inner, rhs.inner)
491            }
492        }
493    };
494}
495forward_assign_op!(RemAssign::rem_assign);
496forward_assign_op!(AddAssign::add_assign);
497forward_assign_op!(SubAssign::sub_assign);
498forward_assign_op!(BitAndAssign::bitand_assign);
499forward_assign_op!(BitOrAssign::bitor_assign);
500forward_assign_op!(BitXorAssign::bitxor_assign);
501
502/// Q *= Q'
503///
504/// ```
505/// # use dsp_fixedpoint::Q8;
506/// let mut q = Q8::<4>::from_f32(0.25);
507/// q *= Q8::<3>::from_int(3);
508/// assert_eq!(q, Q8::from_f32(0.75));
509/// ```
510impl<T: Copy + Accu<A>, A: Shift + Mul<A, Output = A>, const F: i8, const F1: i8>
511    MulAssign<Q<T, A, F1>> for Q<T, A, F>
512{
513    #[inline]
514    fn mul_assign(&mut self, rhs: Q<T, A, F1>) {
515        self.inner = T::down((self.inner.up() * rhs.inner.up()).shs(-F1));
516    }
517}
518
519/// Q /= Q'
520///
521/// ```
522/// # use dsp_fixedpoint::Q8;
523/// let mut q = Q8::<4>::from_f32(0.75);
524/// q /= Q8::<3>::from_int(3);
525/// assert_eq!(q, Q8::from_f32(0.25));
526/// ```
527impl<
528    T: Copy + Shift + Accu<A> + Div<T, Output = T>,
529    A: Shift + Div<A, Output = A>,
530    const F: i8,
531    const F1: i8,
532> DivAssign<Q<T, A, F1>> for Q<T, A, F>
533{
534    #[inline]
535    fn div_assign(&mut self, rhs: Q<T, A, F1>) {
536        self.inner = if F1 > 0 {
537            T::down(self.inner.up().shs(F1) / rhs.inner.up())
538        } else {
539            self.inner.shs(F1) / rhs.inner
540        };
541    }
542}
543
544/// Q*Q -> Q
545///
546/// ```
547/// # use dsp_fixedpoint::Q8;
548/// assert_eq!(
549///     Q8::<4>::from_f32(0.75) * Q8::from_int(3),
550///     Q8::from_f32(2.25)
551/// );
552/// ```
553impl<T, A, const F: i8> Mul for Q<T, A, F>
554where
555    Self: MulAssign,
556{
557    type Output = Self;
558    #[inline]
559    fn mul(mut self, rhs: Self) -> Self::Output {
560        self *= rhs;
561        self
562    }
563}
564
565/// Q/Q -> Q
566///
567/// ```
568/// # use dsp_fixedpoint::Q8;
569/// assert_eq!(Q8::<4>::from_int(3) / Q8::from_int(2), Q8::from_f32(1.5));
570/// ```
571impl<T, A, const F: i8> Div for Q<T, A, F>
572where
573    Self: DivAssign,
574{
575    type Output = Self;
576    #[inline]
577    fn div(mut self, rhs: Self) -> Self::Output {
578        self /= rhs;
579        self
580    }
581}
582
583impl<T: iter::Sum, A, const F: i8> iter::Sum for Q<T, A, F> {
584    #[inline]
585    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
586        Self::new(iter.map(|i| i.inner).sum())
587    }
588}
589
590/// ```
591/// # use dsp_fixedpoint::Q8;
592/// let q = Q8::<4>::new(7);
593/// assert_eq!(format!("{q} {q:e} {q:E}"), "0.4375 4.375e-1 4.375E-1");
594/// ```
595macro_rules! impl_fmt {
596    ($tr:path) => {
597        impl<T, A, const F: i8> $tr for Q<T, A, F>
598        where
599            Self: Copy + AsPrimitive<f64>,
600        {
601            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
602                <f64 as $tr>::fmt(&(*self).as_(), f)
603            }
604        }
605    };
606}
607impl_fmt!(fmt::Display);
608impl_fmt!(fmt::UpperExp);
609impl_fmt!(fmt::LowerExp);
610
611/// ```
612/// # use dsp_fixedpoint::Q8;
613/// assert_eq!(format!("{:?}", Q8::<4>::new(0x14)), "20");
614/// assert_eq!(format!("{:b}", Q8::<4>::new(0x14)), "10100");
615/// assert_eq!(format!("{:b}", Q8::<4>::new(-0x14)), "11101100");
616/// ```
617macro_rules! impl_dot_fmt {
618    ($tr:path) => {
619        impl<T: $tr, A, const F: i8> $tr for Q<T, A, F> {
620            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
621                self.inner.fmt(f)
622            }
623        }
624    };
625}
626impl_dot_fmt!(fmt::Debug);
627impl_dot_fmt!(fmt::Binary);
628impl_dot_fmt!(fmt::Octal);
629impl_dot_fmt!(fmt::UpperHex);
630impl_dot_fmt!(fmt::LowerHex);
631
632// TODO: dot format
633
634macro_rules! impl_q {
635    // Primitive
636    ($alias:ident<$t:ty, $a:ty>) => {
637        impl_q!($alias<$t, $a>, $t, |x| x as _, core::convert::identity);
638    };
639    // Newtype
640    ($alias:ident<$t:ty, $a:ty>, $wrap:tt) => {
641        impl_q!($alias<$wrap<$t>, $wrap<$a>>, $t, |x: $wrap<_>| $wrap(x.0 as _), $wrap);
642    };
643    // Common
644    ($alias:ident<$t:ty, $a:ty>, $inner:ty, $as:expr, $wrap:expr) => {
645        impl Accu<$a> for $t {
646            #[inline(always)]
647            fn up(self) -> $a {
648                $as(self)
649            }
650            #[inline(always)]
651            fn down(a: $a) -> Self {
652                $as(a)
653            }
654        }
655
656        #[doc = concat!("Fixed point [`", stringify!($t), "`] with [`", stringify!($a), "`] accumulator")]
657        pub type $alias<const F: i8> = Q<$t, $a, F>;
658
659        impl<const F: i8> ConstOne for Q<$t, $a, F> {
660            const ONE: Self = Self::new($wrap(if F >= 0 {1 << F as usize} else {0}));
661        }
662
663        impl<const F: i8> AsPrimitive<$t> for Q<$a, $t, F> {
664            /// Scale from integer accu type
665            #[inline]
666            fn as_(self) -> $t {
667                self.quantize()
668            }
669        }
670
671        /// T*Q -> T
672        impl<const F: i8> Mul<Q<$t, $a, F>> for $t {
673            type Output = $t;
674
675            #[inline]
676            fn mul(self, rhs: Q<$t, $a, F>) -> Self::Output {
677                (rhs * self).quantize()
678            }
679        }
680
681        /// T/Q -> T
682        impl<const F: i8> Div<Q<$t, $a, F>> for $t {
683            type Output = $t;
684
685            #[inline]
686            fn div(self, rhs: Q<$t, $a, F>) -> Self::Output {
687                if F > 0 {
688                    <$t>::down(self.up().shs(F) / rhs.inner.up())
689                } else {
690                    self.shs(F) / rhs.inner
691                }
692            }
693        }
694    };
695}
696// Signed
697impl_q!(Q8<i8, i16>);
698impl_q!(Q16<i16, i32>);
699impl_q!(Q32<i32, i64>);
700impl_q!(Q64<i64, i128>);
701// Unsigned (_P_ositive)
702impl_q!(P8<u8, u16>);
703impl_q!(P16<u16, u32>);
704impl_q!(P32<u32, u64>);
705impl_q!(P64<u64, u128>);
706// _W_rapping signed
707impl_q!(W8<i8, i16>, Wrapping);
708impl_q!(W16<i16, i32>, Wrapping);
709impl_q!(W32<i32, i64>, Wrapping);
710impl_q!(W64<i64, i128>, Wrapping);
711// Wrapping vnsigned
712impl_q!(V8<u8, u16>, Wrapping);
713impl_q!(V16<u16, u32>, Wrapping);
714impl_q!(V32<u32, u64>, Wrapping);
715impl_q!(V64<u64, u128>, Wrapping);
716
717// NonZero<T>, Saturating<T> don't implement Shr/Shl
718
719#[cfg(test)]
720mod test {
721    use super::*;
722
723    #[test]
724    fn simple() {
725        assert_eq!(
726            Q32::<5>::from_int(4) * Q32::<5>::from_int(3),
727            Q32::from_int(3 * 4)
728        );
729        assert_eq!(
730            Q32::<5>::from_int(12) / Q32::<5>::from_int(6),
731            Q32::from_int(2)
732        );
733        assert_eq!(7 * Q32::<4>::new(0x33), 7 * 3 + ((3 * 7) >> 4));
734    }
735
736    #[test]
737    fn display() {
738        assert_eq!(format!("{}", Q32::<9>::new(0x12345)), "145.634765625");
739        assert_eq!(format!("{}", Q32::<9>::from_int(99)), "99");
740    }
741}