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