Skip to main content

dsp_fixedpoint/
num_traits_impl.rs

1use core::num::Wrapping;
2use core::ops::Add;
3use num_traits::{
4    AsPrimitive, Bounded, ConstZero, FromPrimitive, Num, One, Signed, ToPrimitive, Zero,
5};
6
7#[cfg(not(feature = "std"))]
8#[allow(unused_imports)]
9use num_traits::float::FloatCore; // .round()
10
11use crate::{Accu, AsFloat, Q, Shift};
12
13impl<T: Zero, A, const F: i8> Zero for Q<T, A, F>
14where
15    Self: Add<Output = Self>,
16{
17    fn zero() -> Self {
18        Self::new(T::zero())
19    }
20
21    fn is_zero(&self) -> bool {
22        self.inner.is_zero()
23    }
24}
25
26impl<T: ConstZero, A, const F: i8> ConstZero for Q<T, A, F> {
27    const ZERO: Self = Self::new(T::ZERO);
28}
29
30macro_rules! impl_as_float {
31    ($ty:ident) => {
32        impl<T, A, const F: i8> AsPrimitive<Q<T, A, F>> for $ty
33        where
34            $ty: AsPrimitive<T>,
35            T: 'static + Copy,
36            A: 'static,
37        {
38            #[inline]
39            fn as_(self) -> Q<T, A, F> {
40                Q::new(
41                    (self * const { 1.0 / Q::<T, A, F>::DELTA as $ty })
42                        .round()
43                        .as_(),
44                )
45            }
46        }
47
48        impl<T, A, const F: i8> AsPrimitive<$ty> for Q<T, A, F>
49        where
50            T: AsPrimitive<$ty>,
51            A: 'static,
52        {
53            #[inline]
54            fn as_(self) -> $ty {
55                self.inner.as_() * Self::DELTA as $ty
56            }
57        }
58    };
59}
60
61impl_as_float!(f32);
62impl_as_float!(f64);
63
64impl<T, A, const F: i8> AsPrimitive<Self> for Q<T, A, F>
65where
66    Self: Copy + 'static,
67{
68    #[inline]
69    fn as_(self) -> Self {
70        self
71    }
72}
73
74macro_rules! impl_accu_as_primitive {
75    ($($t:ty => $a:ty),* $(,)?) => {
76        $(
77            impl<const F: i8> AsPrimitive<$t> for Q<$a, $t, F> {
78                #[inline]
79                fn as_(self) -> $t {
80                    self.quantize()
81                }
82            }
83        )*
84    };
85}
86
87impl_accu_as_primitive!(
88    i8 => i16,
89    i16 => i32,
90    i32 => i64,
91    i64 => i128,
92    u8 => u16,
93    u16 => u32,
94    u32 => u64,
95    u64 => u128,
96    Wrapping<i8> => Wrapping<i16>,
97    Wrapping<i16> => Wrapping<i32>,
98    Wrapping<i32> => Wrapping<i64>,
99    Wrapping<i64> => Wrapping<i128>,
100    Wrapping<u8> => Wrapping<u16>,
101    Wrapping<u16> => Wrapping<u32>,
102    Wrapping<u32> => Wrapping<u64>,
103    Wrapping<u64> => Wrapping<u128>,
104);
105
106impl<T: Bounded, A, const F: i8> Bounded for Q<T, A, F> {
107    #[inline]
108    fn min_value() -> Self {
109        Self::new(T::min_value())
110    }
111
112    #[inline]
113    fn max_value() -> Self {
114        Self::new(T::max_value())
115    }
116}
117
118impl<T, A, const F: i8> Num for Q<T, A, F>
119where
120    T: Num + Shift + Accu<A> + Copy + core::ops::Div<T, Output = T>,
121    A: Shift + Copy + core::ops::Div<A, Output = A>,
122    Self: One + Zero,
123{
124    type FromStrRadixErr = T::FromStrRadixErr;
125
126    #[inline]
127    fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> {
128        T::from_str_radix(str, radix).map(Self::from_int)
129    }
130}
131
132// Integer conversions use the same shifts as DSP arithmetic. Reversing a left
133// shift detects lost high bits; right shifts deliberately discard low bits.
134#[inline]
135fn checked_scale<T: Shift + PartialEq>(value: T, shift: i8) -> Option<T> {
136    assert!(shift > i8::MIN, "shift must not be i8::MIN");
137    let scaled = value.shs(shift);
138    (shift <= 0 || scaled.shs(-shift) == value).then_some(scaled)
139}
140
141impl<T: Copy + ToPrimitive + AsFloat, A, const F: i8> ToPrimitive for Q<T, A, F> {
142    #[inline]
143    fn to_i64(&self) -> Option<i64> {
144        self.to_i128()?.to_i64()
145    }
146
147    #[inline]
148    fn to_i128(&self) -> Option<i128> {
149        checked_scale(self.inner.to_i128()?, const { -F })
150    }
151
152    #[inline]
153    fn to_u64(&self) -> Option<u64> {
154        self.to_u128()?.to_u64()
155    }
156
157    #[inline]
158    fn to_u128(&self) -> Option<u128> {
159        checked_scale(self.inner.to_u128()?, const { -F })
160    }
161
162    #[inline]
163    fn to_f32(&self) -> Option<f32> {
164        Some((*self).as_f32())
165    }
166
167    #[inline]
168    fn to_f64(&self) -> Option<f64> {
169        Some((*self).as_f64())
170    }
171}
172
173impl<T, A, const F: i8> FromPrimitive for Q<T, A, F>
174where
175    T: FromPrimitive,
176{
177    #[inline]
178    fn from_i64(n: i64) -> Option<Self> {
179        Self::from_i128(n as i128)
180    }
181
182    #[inline]
183    fn from_i128(n: i128) -> Option<Self> {
184        T::from_i128(checked_scale(n, F)?).map(Self::new)
185    }
186
187    #[inline]
188    fn from_u64(n: u64) -> Option<Self> {
189        Self::from_u128(n as u128)
190    }
191
192    #[inline]
193    fn from_u128(n: u128) -> Option<Self> {
194        T::from_u128(checked_scale(n, F)?).map(Self::new)
195    }
196
197    #[inline]
198    fn from_f32(n: f32) -> Option<Self> {
199        T::from_f32((n * const { 1.0 / Self::DELTA }).round()).map(Self::new)
200    }
201
202    #[inline]
203    fn from_f64(n: f64) -> Option<Self> {
204        T::from_f64((n * const { 1.0 / Self::DELTA as f64 }).round()).map(Self::new)
205    }
206}
207
208macro_rules! impl_signed_q {
209    ($($ty:ty),* $(,)?) => {
210        $(
211            impl<A, const F: i8> Signed for Q<$ty, A, F>
212            where
213                Self: Num + core::ops::Neg<Output = Self>,
214            {
215                #[inline]
216                fn abs(&self) -> Self {
217                    Self::new(self.inner.abs())
218                }
219
220                #[inline]
221                fn abs_sub(&self, other: &Self) -> Self {
222                    Self::new(self.inner.abs_sub(&other.inner))
223                }
224
225                #[inline]
226                fn signum(&self) -> Self {
227                    match Signed::signum(&self.inner) {
228                        1 => Self::one(),
229                        -1 => -Self::one(),
230                        _ => Self::zero(),
231                    }
232                }
233
234                #[inline]
235                fn is_positive(&self) -> bool {
236                    self.inner > 0
237                }
238
239                #[inline]
240                fn is_negative(&self) -> bool {
241                    self.inner < 0
242                }
243            }
244
245            impl<A, const F: i8> Signed for Q<Wrapping<$ty>, A, F>
246            where
247                Self: Num + core::ops::Neg<Output = Self>,
248            {
249                #[inline]
250                fn abs(&self) -> Self {
251                    Self::new(Wrapping(self.inner.0.abs()))
252                }
253
254                #[inline]
255                fn abs_sub(&self, other: &Self) -> Self {
256                    Self::new(self.inner.abs_sub(&other.inner))
257                }
258
259                #[inline]
260                fn signum(&self) -> Self {
261                    match Signed::signum(&self.inner) {
262                        Wrapping(1) => Self::one(),
263                        Wrapping(-1) => -Self::one(),
264                        Wrapping(_) => Self::zero(),
265                    }
266                }
267
268                #[inline]
269                fn is_positive(&self) -> bool {
270                    self.inner.0.is_positive()
271                }
272
273                #[inline]
274                fn is_negative(&self) -> bool {
275                    self.inner.0.is_negative()
276                }
277            }
278        )*
279    };
280}
281
282impl_signed_q!(i8, i16, i32, i64);