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 sqrt(self) -> Self;
9    fn ln(self) -> Self;
10    fn exp(self) -> Self;
11    fn atan2(self, x: Self) -> Self;
12    fn powf(self, n: Self) -> Self;
13    fn tanh(self) -> Self;
14}
15
16impl FloatMath for f32 {
17    #[inline(always)]
18    fn abs(self) -> f32 {
19        #[cfg(feature = "std")]
20        {
21            self.abs()
22        }
23        #[cfg(all(not(feature = "std"), feature = "libm"))]
24        {
25            libm::fabsf(self)
26        }
27        #[cfg(all(not(feature = "std"), not(feature = "libm")))]
28        {
29            f32::from_bits(self.to_bits() & 0x7FFF_FFFF)
30        }
31    }
32
33    #[inline(always)]
34    fn sin(self) -> f32 {
35        #[cfg(feature = "std")]
36        {
37            self.sin()
38        }
39        #[cfg(all(not(feature = "std"), feature = "libm"))]
40        {
41            libm::sinf(self)
42        }
43        #[cfg(all(not(feature = "std"), not(feature = "libm")))]
44        {
45            core::compile_error!("Either feature 'std' or feature 'libm' must be enabled for floating-point trig functions.");
46        }
47    }
48
49    #[inline(always)]
50    fn cos(self) -> f32 {
51        #[cfg(feature = "std")]
52        {
53            self.cos()
54        }
55        #[cfg(all(not(feature = "std"), feature = "libm"))]
56        {
57            libm::cosf(self)
58        }
59        #[cfg(all(not(feature = "std"), not(feature = "libm")))]
60        {
61            core::compile_error!("Either feature 'std' or feature 'libm' must be enabled for floating-point trig functions.");
62        }
63    }
64
65    #[inline(always)]
66    fn sqrt(self) -> f32 {
67        #[cfg(feature = "std")]
68        {
69            self.sqrt()
70        }
71        #[cfg(all(not(feature = "std"), feature = "libm"))]
72        {
73            libm::sqrtf(self)
74        }
75        #[cfg(all(not(feature = "std"), not(feature = "libm")))]
76        {
77            core::compile_error!(
78                "Either feature 'std' or feature 'libm' must be enabled for floating-point sqrt."
79            );
80        }
81    }
82
83    #[inline(always)]
84    fn ln(self) -> f32 {
85        #[cfg(feature = "std")]
86        {
87            self.ln()
88        }
89        #[cfg(all(not(feature = "std"), feature = "libm"))]
90        {
91            libm::logf(self)
92        }
93        #[cfg(all(not(feature = "std"), not(feature = "libm")))]
94        {
95            core::compile_error!(
96                "Either feature 'std' or feature 'libm' must be enabled for floating-point log."
97            );
98        }
99    }
100
101    #[inline(always)]
102    fn exp(self) -> f32 {
103        #[cfg(feature = "std")]
104        {
105            self.exp()
106        }
107        #[cfg(all(not(feature = "std"), feature = "libm"))]
108        {
109            libm::expf(self)
110        }
111        #[cfg(all(not(feature = "std"), not(feature = "libm")))]
112        {
113            core::compile_error!(
114                "Either feature 'std' or feature 'libm' must be enabled for floating-point exp."
115            );
116        }
117    }
118
119    #[inline(always)]
120    fn atan2(self, x: f32) -> f32 {
121        #[cfg(feature = "std")]
122        {
123            self.atan2(x)
124        }
125        #[cfg(all(not(feature = "std"), feature = "libm"))]
126        {
127            libm::atan2f(self, x)
128        }
129        #[cfg(all(not(feature = "std"), not(feature = "libm")))]
130        {
131            core::compile_error!(
132                "Either feature 'std' or feature 'libm' must be enabled for floating-point atan2."
133            );
134        }
135    }
136
137    #[inline(always)]
138    fn powf(self, n: f32) -> f32 {
139        #[cfg(feature = "std")]
140        {
141            self.powf(n)
142        }
143        #[cfg(all(not(feature = "std"), feature = "libm"))]
144        {
145            libm::powf(self, n)
146        }
147        #[cfg(all(not(feature = "std"), not(feature = "libm")))]
148        {
149            core::compile_error!(
150                "Either feature 'std' or feature 'libm' must be enabled for floating-point powf."
151            );
152        }
153    }
154
155    #[inline(always)]
156    fn tanh(self) -> f32 {
157        #[cfg(feature = "std")]
158        {
159            self.tanh()
160        }
161        #[cfg(all(not(feature = "std"), feature = "libm"))]
162        {
163            libm::tanhf(self)
164        }
165        #[cfg(all(not(feature = "std"), not(feature = "libm")))]
166        {
167            core::compile_error!(
168                "Either feature 'std' or feature 'libm' must be enabled for floating-point tanh."
169            );
170        }
171    }
172}
173
174impl FloatMath for f64 {
175    #[inline(always)]
176    fn abs(self) -> f64 {
177        #[cfg(feature = "std")]
178        {
179            self.abs()
180        }
181        #[cfg(all(not(feature = "std"), feature = "libm"))]
182        {
183            libm::fabs(self)
184        }
185        #[cfg(all(not(feature = "std"), not(feature = "libm")))]
186        {
187            f64::from_bits(self.to_bits() & 0x7FFF_FFFF_FFFF_FFFF)
188        }
189    }
190
191    #[inline(always)]
192    fn sin(self) -> f64 {
193        #[cfg(feature = "std")]
194        {
195            self.sin()
196        }
197        #[cfg(all(not(feature = "std"), feature = "libm"))]
198        {
199            libm::sin(self)
200        }
201        #[cfg(all(not(feature = "std"), not(feature = "libm")))]
202        {
203            core::compile_error!("Either feature 'std' or feature 'libm' must be enabled for floating-point trig functions.");
204        }
205    }
206
207    #[inline(always)]
208    fn cos(self) -> f64 {
209        #[cfg(feature = "std")]
210        {
211            self.cos()
212        }
213        #[cfg(all(not(feature = "std"), feature = "libm"))]
214        {
215            libm::cos(self)
216        }
217        #[cfg(all(not(feature = "std"), not(feature = "libm")))]
218        {
219            core::compile_error!("Either feature 'std' or feature 'libm' must be enabled for floating-point trig functions.");
220        }
221    }
222
223    #[inline(always)]
224    fn sqrt(self) -> f64 {
225        #[cfg(feature = "std")]
226        {
227            self.sqrt()
228        }
229        #[cfg(all(not(feature = "std"), feature = "libm"))]
230        {
231            libm::sqrt(self)
232        }
233        #[cfg(all(not(feature = "std"), not(feature = "libm")))]
234        {
235            core::compile_error!(
236                "Either feature 'std' or feature 'libm' must be enabled for floating-point sqrt."
237            );
238        }
239    }
240
241    #[inline(always)]
242    fn ln(self) -> f64 {
243        #[cfg(feature = "std")]
244        {
245            self.ln()
246        }
247        #[cfg(all(not(feature = "std"), feature = "libm"))]
248        {
249            libm::log(self)
250        }
251        #[cfg(all(not(feature = "std"), not(feature = "libm")))]
252        {
253            core::compile_error!(
254                "Either feature 'std' or feature 'libm' must be enabled for floating-point log."
255            );
256        }
257    }
258
259    #[inline(always)]
260    fn exp(self) -> f64 {
261        #[cfg(feature = "std")]
262        {
263            self.exp()
264        }
265        #[cfg(all(not(feature = "std"), feature = "libm"))]
266        {
267            libm::exp(self)
268        }
269        #[cfg(all(not(feature = "std"), not(feature = "libm")))]
270        {
271            core::compile_error!(
272                "Either feature 'std' or feature 'libm' must be enabled for floating-point exp."
273            );
274        }
275    }
276
277    #[inline(always)]
278    fn atan2(self, x: f64) -> f64 {
279        #[cfg(feature = "std")]
280        {
281            self.atan2(x)
282        }
283        #[cfg(all(not(feature = "std"), feature = "libm"))]
284        {
285            libm::atan2(self, x)
286        }
287        #[cfg(all(not(feature = "std"), not(feature = "libm")))]
288        {
289            core::compile_error!(
290                "Either feature 'std' or feature 'libm' must be enabled for floating-point atan2."
291            );
292        }
293    }
294
295    #[inline(always)]
296    fn powf(self, n: f64) -> f64 {
297        #[cfg(feature = "std")]
298        {
299            self.powf(n)
300        }
301        #[cfg(all(not(feature = "std"), feature = "libm"))]
302        {
303            libm::pow(self, n)
304        }
305        #[cfg(all(not(feature = "std"), not(feature = "libm")))]
306        {
307            core::compile_error!(
308                "Either feature 'std' or feature 'libm' must be enabled for floating-point powf."
309            );
310        }
311    }
312
313    #[inline(always)]
314    fn tanh(self) -> f64 {
315        #[cfg(feature = "std")]
316        {
317            self.tanh()
318        }
319        #[cfg(all(not(feature = "std"), feature = "libm"))]
320        {
321            libm::tanh(self)
322        }
323        #[cfg(all(not(feature = "std"), not(feature = "libm")))]
324        {
325            core::compile_error!(
326                "Either feature 'std' or feature 'libm' must be enabled for floating-point tanh."
327            );
328        }
329    }
330}