Skip to main content

dsp_fixedpoint/
lib.rs

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
19/// Construct a value from the ratio of two raw values.
20///
21/// Division rounds toward zero. For `Q<T, A, F>`, the denominator must be
22/// nonzero, the result must fit `T`, and the operand scaled by `2^|F|` must
23/// fit `A`: the numerator for positive `F`, the denominator for negative `F`.
24pub trait FromRatio<T> {
25    /// Return `numerator / denominator` in `Self`'s representation.
26    fn from_ratio(numerator: T, denominator: T) -> Self;
27
28    /// Divide several numerators by one denominator.
29    ///
30    /// Implementations may prepare the denominator once; floating-point types
31    /// multiply every numerator by one reciprocal.
32    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
67/// Helper trait to unify over missing impl AsPrimitive<f*> for Wrapping<T>
68pub(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
105/// Shift summary trait
106///
107/// Wrapping supports `Sh{lr}<usize>` only.
108pub trait Shift: Copy + Shl<usize, Output = Self> + Shr<usize, Output = Self> {
109    /// Signed shift (positive: left)
110    ///
111    /// `x*2**f`
112    ///
113    /// ```
114    /// # use dsp_fixedpoint::Shift;
115    /// assert_eq!(1i32.shs(1), 2);
116    /// assert_eq!(4i32.shs(-1), 2);
117    /// ```
118    fn shs(self, f: i8) -> Self;
119
120    /// Const signed shift
121    #[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
140/// Conversion trait between base and accumulator type
141pub trait Accu<A> {
142    /// Cast up to accumulator type
143    ///
144    /// This is a primitive cast.
145    ///
146    /// ```
147    /// # use dsp_fixedpoint::Accu;
148    /// assert_eq!(3i32.up(), 3i64);
149    /// ```
150    fn up(self) -> A;
151
152    /// Cast down from accumulator type
153    ///
154    /// This is a primitive cast.
155    ///
156    /// ```
157    /// # use dsp_fixedpoint::Accu;
158    /// assert_eq!(i16::down(3i32), 3i16);
159    /// ```
160    fn down(a: A) -> Self;
161
162    // /// Cast to f32
163    // fn as_f32(self) -> f32;
164    // /// Cast to f64
165    // fn as_f64(self) -> f64;
166    // /// Cast from f32
167    // fn f32_as(value: f64) -> Self;
168    // /// Cast from f64
169    // fn f64_as(value: f64) -> Self;
170}
171
172/// Fixed-point value with storage `T`, accumulator `A`, and `F` fractional bits.
173///
174/// Generics:
175/// * `T`: Base integer
176/// * `A`: Accumulator for intermediate results
177/// * `F`: Number of fractional bits right of the decimal point
178///
179/// `F` negative is supported analogously.
180///
181/// * `Q32<31>` is `(-1..1).step_by(2^-31)`
182/// * `Q<i16, _, 20>` is `(-1/32..1/32).step_by(2^-20)`
183/// * `Q<u8, _, 4>` is `(0..16).step_by(1/16)`
184/// * `Q<u8, _, -2>` is `(0..1024).step_by(4)`
185///
186/// ```
187/// # use dsp_fixedpoint::Q8;
188/// assert_eq!(Q8::<4>::from_int(3), Q8::from_bits(3 << 4));
189/// assert_eq!(7 * Q8::<4>::from_f32(1.5), 10);
190/// assert_eq!(Q8::<4>::from_f32(1.5).apply(7), 10);
191/// assert_eq!(7 / Q8::<4>::from_f32(1.5), 4);
192/// ```
193#[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    /// The accumulator type
205    _accu: PhantomData<A>,
206    /// The inner value representation
207    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    /// Step between distinct numbers
257    ///
258    /// ```
259    /// # use dsp_fixedpoint::Q32;
260    /// assert_eq!(Q32::<31>::DELTA, 2f32.powi(-31));
261    /// assert_eq!(Q32::<-4>::DELTA, 2f32.powi(4));
262    /// ```
263    ///
264    /// ```compile_fail
265    /// # use dsp_fixedpoint::Q32;
266    /// let _ = Q32::<-128>::DELTA;
267    /// ```
268    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    /// Create a new fixed point number from a raw representation.
283    #[inline]
284    pub const fn from_bits(bits: T) -> Self {
285        Self::new(bits)
286    }
287
288    /// Return the raw representation.
289    #[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    /// Convert to a different number of fractional bits (truncating)
298    ///
299    /// Use this liberally for Add/Sub/Rem with Q's of different F.
300    ///
301    /// ```
302    /// # use dsp_fixedpoint::Q8;
303    /// assert_eq!(Q8::<4>::from_bits(32).scale::<0>(), Q8::from_bits(2));
304    /// ```
305    #[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    /// Return the integer part
311    ///
312    /// ```
313    /// # use dsp_fixedpoint::Q8;
314    /// assert_eq!(Q8::<4>::from_bits(0x35).trunc(), 0x3);
315    /// ```
316    #[inline]
317    #[must_use]
318    pub fn trunc(self) -> T {
319        self.inner.shs(const { -F })
320    }
321
322    /// Scale from integer base type
323    ///
324    /// ```
325    /// # use dsp_fixedpoint::Q8;
326    /// assert_eq!(Q8::<4>::from_int(7).into_bits(), 7 << 4);
327    /// ```
328    #[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    /// Scale from integer accu type
336    ///
337    ///
338    /// ```
339    /// # use dsp_fixedpoint::Q8;
340    /// let q = Q8::<4>::from_f32(0.25);
341    /// assert_eq!((q * 7).quantize(), (7.0 * 0.25f32).floor() as _);
342    /// ```
343    #[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    /// Multiply a coefficient by a raw value without quantizing.
352    ///
353    /// Accumulate the returned `Q<A, T, F>` values, then call [`Q::quantize`]
354    /// once.
355    ///
356    /// ```
357    /// # use dsp_fixedpoint::{Q, Q8};
358    /// assert_eq!(Q8::<3>::from_bits(4).mul_wide(2), Q::from_bits(8));
359    /// ```
360    #[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    /// Apply this fixed-point value as a gain to a raw integer and quantize.
387    ///
388    /// ```
389    /// # use dsp_fixedpoint::Q8;
390    /// assert_eq!(Q8::<4>::from_f32(0.25).apply(7), 1);
391    /// ```
392    #[inline]
393    #[must_use]
394    pub fn apply(self, rhs: T) -> T {
395        self.mul_wide(rhs).quantize()
396    }
397}
398
399/// Lossy conversion from a dynamically scaled integer
400///
401/// ```
402/// # use dsp_fixedpoint::Q8;
403/// assert_eq!(Q8::<8>::from((1, 3)).into_bits(), 1 << 5);
404/// ```
405impl<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
411/// Lossless conversion into a dynamically scaled integer
412///
413/// ```
414/// # use dsp_fixedpoint::Q8;
415/// let q: (i8, i8) = Q8::<8>::from_bits(9).into();
416/// assert_eq!(q, (9, 8));
417/// ```
418impl<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    /// Quantize a f32
430    #[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    /// Quantize a f64
442    #[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    /// Convert lossy to f32
451    #[inline]
452    #[must_use]
453    pub fn as_f32(self) -> f32 {
454        self.inner.as_f32() * Self::DELTA
455    }
456
457    /// Convert lossy to f64
458    #[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    // Primitive
467    ($alias:ident<$t:ty, $a:ty>) => {
468        impl_q!($alias<$t, $a>, $t, |x| x as _, core::convert::identity);
469    };
470    // Newtype
471    ($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    // Common
475    ($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        /// T*Q -> T
519        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        /// T/Q -> T
529        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}
539// Signed
540impl_q!(Q8<i8, i16>);
541impl_q!(Q16<i16, i32>);
542impl_q!(Q32<i32, i64>);
543impl_q!(Q64<i64, i128>);
544// Unsigned (_P_ositive)
545impl_q!(P8<u8, u16>);
546impl_q!(P16<u16, u32>);
547impl_q!(P32<u32, u64>);
548impl_q!(P64<u64, u128>);
549// _W_rapping signed
550impl_q!(W8<i8, i16>, Wrapping);
551impl_q!(W16<i16, i32>, Wrapping);
552impl_q!(W32<i32, i64>, Wrapping);
553impl_q!(W64<i64, i128>, Wrapping);
554// Wrapping vnsigned
555impl_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// NonZero<T>, Saturating<T> don't implement Shr/Shl
561
562#[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}