Skip to main content

embedded_dsp/
math.rs

1//! Unified math abstraction providing floating-point mathematical operations using `libm` in `no_std` environments or `std` math when available.
2
3/// Floating-point math trait providing `sin`, `cos`, `sqrt`, `ln`, `exp`, `atan2`, `powf`, `tanh`, `abs`.
4pub trait FloatMath: Sized {
5    fn abs(self) -> Self;
6    fn sin(self) -> Self;
7    fn cos(self) -> Self;
8    fn tan(self) -> Self;
9    fn sqrt(self) -> Self;
10    fn ln(self) -> Self;
11    fn log10(self) -> Self;
12    fn exp(self) -> Self;
13    fn atan2(self, x: Self) -> Self;
14    fn powf(self, n: Self) -> Self;
15    fn tanh(self) -> Self;
16}
17
18impl FloatMath for f32 {
19    #[inline(always)]
20    fn abs(self) -> f32 {
21        #[cfg(feature = "std")]
22        {
23            self.abs()
24        }
25        #[cfg(all(not(feature = "std"), feature = "libm"))]
26        {
27            libm::fabsf(self)
28        }
29        #[cfg(all(not(feature = "std"), not(feature = "libm")))]
30        {
31            f32::from_bits(self.to_bits() & 0x7FFF_FFFF)
32        }
33    }
34
35    #[inline(always)]
36    fn sin(self) -> f32 {
37        #[cfg(feature = "std")]
38        {
39            self.sin()
40        }
41        #[cfg(all(not(feature = "std"), feature = "libm"))]
42        {
43            libm::sinf(self)
44        }
45        #[cfg(all(not(feature = "std"), not(feature = "libm")))]
46        {
47            core::compile_error!(
48                "Either feature 'std' or feature 'libm' must be enabled for floating-point trig functions."
49            );
50        }
51    }
52
53    #[inline(always)]
54    fn cos(self) -> f32 {
55        #[cfg(feature = "std")]
56        {
57            self.cos()
58        }
59        #[cfg(all(not(feature = "std"), feature = "libm"))]
60        {
61            libm::cosf(self)
62        }
63        #[cfg(all(not(feature = "std"), not(feature = "libm")))]
64        {
65            core::compile_error!(
66                "Either feature 'std' or feature 'libm' must be enabled for floating-point trig functions."
67            );
68        }
69    }
70
71    #[inline(always)]
72    fn tan(self) -> f32 {
73        #[cfg(feature = "std")]
74        {
75            self.tan()
76        }
77        #[cfg(all(not(feature = "std"), feature = "libm"))]
78        {
79            libm::tanf(self)
80        }
81        #[cfg(all(not(feature = "std"), not(feature = "libm")))]
82        {
83            core::compile_error!(
84                "Either feature 'std' or feature 'libm' must be enabled for floating-point trig functions."
85            );
86        }
87    }
88
89    #[inline(always)]
90    fn sqrt(self) -> f32 {
91        #[cfg(feature = "std")]
92        {
93            self.sqrt()
94        }
95        #[cfg(all(not(feature = "std"), feature = "libm"))]
96        {
97            libm::sqrtf(self)
98        }
99        #[cfg(all(not(feature = "std"), not(feature = "libm")))]
100        {
101            core::compile_error!(
102                "Either feature 'std' or feature 'libm' must be enabled for floating-point sqrt."
103            );
104        }
105    }
106
107    #[inline(always)]
108    fn ln(self) -> f32 {
109        #[cfg(feature = "std")]
110        {
111            self.ln()
112        }
113        #[cfg(all(not(feature = "std"), feature = "libm"))]
114        {
115            libm::logf(self)
116        }
117        #[cfg(all(not(feature = "std"), not(feature = "libm")))]
118        {
119            core::compile_error!(
120                "Either feature 'std' or feature 'libm' must be enabled for floating-point log."
121            );
122        }
123    }
124
125    #[inline(always)]
126    fn log10(self) -> f32 {
127        #[cfg(feature = "std")]
128        {
129            self.log10()
130        }
131        #[cfg(all(not(feature = "std"), feature = "libm"))]
132        {
133            libm::log10f(self)
134        }
135        #[cfg(all(not(feature = "std"), not(feature = "libm")))]
136        {
137            core::compile_error!(
138                "Either feature 'std' or feature 'libm' must be enabled for floating-point log10."
139            );
140        }
141    }
142
143    #[inline(always)]
144    fn exp(self) -> f32 {
145        #[cfg(feature = "std")]
146        {
147            self.exp()
148        }
149        #[cfg(all(not(feature = "std"), feature = "libm"))]
150        {
151            libm::expf(self)
152        }
153        #[cfg(all(not(feature = "std"), not(feature = "libm")))]
154        {
155            core::compile_error!(
156                "Either feature 'std' or feature 'libm' must be enabled for floating-point exp."
157            );
158        }
159    }
160
161    #[inline(always)]
162    fn atan2(self, x: f32) -> f32 {
163        #[cfg(feature = "std")]
164        {
165            self.atan2(x)
166        }
167        #[cfg(all(not(feature = "std"), feature = "libm"))]
168        {
169            libm::atan2f(self, x)
170        }
171        #[cfg(all(not(feature = "std"), not(feature = "libm")))]
172        {
173            core::compile_error!(
174                "Either feature 'std' or feature 'libm' must be enabled for floating-point atan2."
175            );
176        }
177    }
178
179    #[inline(always)]
180    fn powf(self, n: f32) -> f32 {
181        #[cfg(feature = "std")]
182        {
183            self.powf(n)
184        }
185        #[cfg(all(not(feature = "std"), feature = "libm"))]
186        {
187            libm::powf(self, n)
188        }
189        #[cfg(all(not(feature = "std"), not(feature = "libm")))]
190        {
191            core::compile_error!(
192                "Either feature 'std' or feature 'libm' must be enabled for floating-point powf."
193            );
194        }
195    }
196
197    #[inline(always)]
198    fn tanh(self) -> f32 {
199        #[cfg(feature = "std")]
200        {
201            self.tanh()
202        }
203        #[cfg(all(not(feature = "std"), feature = "libm"))]
204        {
205            libm::tanhf(self)
206        }
207        #[cfg(all(not(feature = "std"), not(feature = "libm")))]
208        {
209            core::compile_error!(
210                "Either feature 'std' or feature 'libm' must be enabled for floating-point tanh."
211            );
212        }
213    }
214}
215
216impl FloatMath for f64 {
217    #[inline(always)]
218    fn abs(self) -> f64 {
219        #[cfg(feature = "std")]
220        {
221            self.abs()
222        }
223        #[cfg(all(not(feature = "std"), feature = "libm"))]
224        {
225            libm::fabs(self)
226        }
227        #[cfg(all(not(feature = "std"), not(feature = "libm")))]
228        {
229            f64::from_bits(self.to_bits() & 0x7FFF_FFFF_FFFF_FFFF)
230        }
231    }
232
233    #[inline(always)]
234    fn sin(self) -> f64 {
235        #[cfg(feature = "std")]
236        {
237            self.sin()
238        }
239        #[cfg(all(not(feature = "std"), feature = "libm"))]
240        {
241            libm::sin(self)
242        }
243        #[cfg(all(not(feature = "std"), not(feature = "libm")))]
244        {
245            core::compile_error!(
246                "Either feature 'std' or feature 'libm' must be enabled for floating-point trig functions."
247            );
248        }
249    }
250
251    #[inline(always)]
252    fn cos(self) -> f64 {
253        #[cfg(feature = "std")]
254        {
255            self.cos()
256        }
257        #[cfg(all(not(feature = "std"), feature = "libm"))]
258        {
259            libm::cos(self)
260        }
261        #[cfg(all(not(feature = "std"), not(feature = "libm")))]
262        {
263            core::compile_error!(
264                "Either feature 'std' or feature 'libm' must be enabled for floating-point trig functions."
265            );
266        }
267    }
268
269    #[inline(always)]
270    fn tan(self) -> f64 {
271        #[cfg(feature = "std")]
272        {
273            self.tan()
274        }
275        #[cfg(all(not(feature = "std"), feature = "libm"))]
276        {
277            libm::tan(self)
278        }
279        #[cfg(all(not(feature = "std"), not(feature = "libm")))]
280        {
281            core::compile_error!(
282                "Either feature 'std' or feature 'libm' must be enabled for floating-point trig functions."
283            );
284        }
285    }
286
287    #[inline(always)]
288    fn sqrt(self) -> f64 {
289        #[cfg(feature = "std")]
290        {
291            self.sqrt()
292        }
293        #[cfg(all(not(feature = "std"), feature = "libm"))]
294        {
295            libm::sqrt(self)
296        }
297        #[cfg(all(not(feature = "std"), not(feature = "libm")))]
298        {
299            core::compile_error!(
300                "Either feature 'std' or feature 'libm' must be enabled for floating-point sqrt."
301            );
302        }
303    }
304
305    #[inline(always)]
306    fn ln(self) -> f64 {
307        #[cfg(feature = "std")]
308        {
309            self.ln()
310        }
311        #[cfg(all(not(feature = "std"), feature = "libm"))]
312        {
313            libm::log(self)
314        }
315        #[cfg(all(not(feature = "std"), not(feature = "libm")))]
316        {
317            core::compile_error!(
318                "Either feature 'std' or feature 'libm' must be enabled for floating-point log."
319            );
320        }
321    }
322
323    #[inline(always)]
324    fn log10(self) -> f64 {
325        #[cfg(feature = "std")]
326        {
327            self.log10()
328        }
329        #[cfg(all(not(feature = "std"), feature = "libm"))]
330        {
331            libm::log10(self)
332        }
333        #[cfg(all(not(feature = "std"), not(feature = "libm")))]
334        {
335            core::compile_error!(
336                "Either feature 'std' or feature 'libm' must be enabled for floating-point log10."
337            );
338        }
339    }
340
341    #[inline(always)]
342    fn exp(self) -> f64 {
343        #[cfg(feature = "std")]
344        {
345            self.exp()
346        }
347        #[cfg(all(not(feature = "std"), feature = "libm"))]
348        {
349            libm::exp(self)
350        }
351        #[cfg(all(not(feature = "std"), not(feature = "libm")))]
352        {
353            core::compile_error!(
354                "Either feature 'std' or feature 'libm' must be enabled for floating-point exp."
355            );
356        }
357    }
358
359    #[inline(always)]
360    fn atan2(self, x: f64) -> f64 {
361        #[cfg(feature = "std")]
362        {
363            self.atan2(x)
364        }
365        #[cfg(all(not(feature = "std"), feature = "libm"))]
366        {
367            libm::atan2(self, x)
368        }
369        #[cfg(all(not(feature = "std"), not(feature = "libm")))]
370        {
371            core::compile_error!(
372                "Either feature 'std' or feature 'libm' must be enabled for floating-point atan2."
373            );
374        }
375    }
376
377    #[inline(always)]
378    fn powf(self, n: f64) -> f64 {
379        #[cfg(feature = "std")]
380        {
381            self.powf(n)
382        }
383        #[cfg(all(not(feature = "std"), feature = "libm"))]
384        {
385            libm::pow(self, n)
386        }
387        #[cfg(all(not(feature = "std"), not(feature = "libm")))]
388        {
389            core::compile_error!(
390                "Either feature 'std' or feature 'libm' must be enabled for floating-point powf."
391            );
392        }
393    }
394
395    #[inline(always)]
396    fn tanh(self) -> f64 {
397        #[cfg(feature = "std")]
398        {
399            self.tanh()
400        }
401        #[cfg(all(not(feature = "std"), feature = "libm"))]
402        {
403            libm::tanh(self)
404        }
405        #[cfg(all(not(feature = "std"), not(feature = "libm")))]
406        {
407            core::compile_error!(
408                "Either feature 'std' or feature 'libm' must be enabled for floating-point tanh."
409            );
410        }
411    }
412}
413
414/// Integer square root of a `u32` (Newton).
415#[inline]
416pub(crate) fn isqrt_u32(n: u32) -> u32 {
417    if n <= 1 {
418        return n;
419    }
420    let mut x = n;
421    let mut y = (x + 1) / 2;
422    while y < x {
423        x = y;
424        y = x.saturating_add(n / x) / 2;
425    }
426    x
427}
428
429/// Integer square root of a `u64` (Newton).
430#[inline]
431pub(crate) fn isqrt_u64(n: u64) -> u64 {
432    if n <= 1 {
433        return n;
434    }
435    let mut x = n;
436    let mut y = (x + 1) / 2;
437    while y < x {
438        x = y;
439        y = x.saturating_add(n / x) / 2;
440    }
441    x
442}