irox_tools/primitives/
f32.rs

1// SPDX-License-Identifier: MIT
2// Copyright 2025 IROX Contributors
3//
4
5//!
6//! A collection of utilities for the f32 built-in
7//!
8
9use crate::{ToF64, ToSigned};
10
11impl crate::f64::FloatExt for f32 {
12    type Type = f32;
13    type Size = u32;
14
15    fn trunc(self) -> f32 {
16        (self as u64) as f32
17    }
18
19    fn fract(self) -> f32 {
20        self - self.trunc()
21    }
22
23    fn abs(self) -> f32 {
24        f32::from_bits(self.to_bits() & 0x7FFF_FFFF)
25    }
26    fn round(self) -> f32 {
27        (self + 0.5 * self.signum()).trunc()
28    }
29
30    fn floor(self) -> f32 {
31        if self.is_sign_negative() {
32            return (self - 1.0).trunc();
33        }
34        self.trunc()
35    }
36
37    fn ceil(self) -> f32 {
38        if self.is_sign_positive() {
39            return (self + 1.0).trunc();
40        }
41        self.trunc()
42    }
43
44    fn signum(self) -> f32 {
45        if self.is_nan() {
46            return f32::NAN;
47        }
48        if self.is_sign_negative() {
49            return -1.0;
50        }
51        1.0
52    }
53    ///
54    /// Implementation of Exponential Function from NIST DTMF eq 4.2.19: `<https://dlmf.nist.gov/4.2.E19>`
55    fn exp(self) -> Self::Type {
56        let mut out = 1.0;
57        let i = self;
58        let mut z = self;
59        let mut exp = 1.0;
60        let mut idx = 1;
61        let mut next = self;
62
63        while next.abs() > f32::EPSILON {
64            out += next;
65            idx += 1;
66            z *= i;
67            if z.is_infinite() {
68                break;
69            }
70            exp *= idx as Self::Type;
71            if exp.is_infinite() {
72                break;
73            }
74            next = z / exp;
75            if next.is_infinite() {
76                break;
77            }
78        }
79
80        out
81    }
82
83    ///
84    /// Implementation of Natural Logarithm using NIST DLMF eq 4.6.4: `<https://dlmf.nist.gov/4.6.E4>`
85    fn ln(self) -> Self::Type {
86        let z = self as f64;
87        let iter = (z - 1.) / (z + 1.);
88        let mut out = 0.0f64;
89        let mut next = iter;
90        let mut base = iter;
91        let mut idx = 1u64;
92        while next.abs() > f64::EPSILON {
93            out += next;
94            idx += 2;
95            base *= iter * iter;
96            next = base / idx as f64;
97        }
98        (out * 2.0) as f32
99    }
100
101    ///
102    /// Implementation of general power function using NIST DLMF eq 4.2.26: `<https://dlmf.nist.gov/4.2.E26>`
103    fn powf(self, a: Self::Type) -> Self::Type {
104        let z = self;
105
106        (a * z.ln()).exp()
107    }
108
109    /// Naive implementation of integer power fn.  Will do something smarter later.
110    fn powi(self, val: i32) -> Self::Type {
111        let mut out = self;
112        let i = self;
113        for _ in 0..val.abs() {
114            out *= i;
115        }
116        out
117    }
118
119    fn sqrt(self) -> Self::Type {
120        self.powf(0.5)
121    }
122
123    fn to_bits(self) -> Self::Size {
124        f32::to_bits(self)
125    }
126
127    fn exponent(self) -> u16 {
128        ((self.to_bits() >> 23) & 0x0F) as u16
129    }
130
131    fn significand(self) -> Self::Size {
132        self.to_bits() & 0x7FFFFF
133    }
134}
135
136impl ToF64 for f32 {
137    fn to_f64(&self) -> f64 {
138        *self as f64
139    }
140}
141
142impl ToSigned for f32 {
143    type Output = f32;
144
145    fn to_signed(self) -> Self::Output {
146        self
147    }
148
149    fn negative_one() -> Self::Output {
150        -1.
151    }
152}
153
154#[cfg(all(test, not(feature = "std")))]
155mod tests {
156    #[test]
157    pub fn test_ln() {
158        assert_eq!(0.0, crate::f64::FloatExt::ln(1.0_f32));
159        assert_eq_eps!(1.0, crate::f64::FloatExt::ln(core::f32::consts::E), 1e-6);
160        assert_eq_eps!(4.6051702, crate::f64::FloatExt::ln(100f32), 1e-6);
161        assert_eq_eps!(
162            11.09033963004403,
163            crate::f64::FloatExt::ln(u16::MAX as f32),
164            1e-6
165        );
166    }
167
168    #[test]
169    pub fn test_exp() {
170        assert_eq_eps!(1.0, crate::f64::FloatExt::exp(0.0f32), 1e-6);
171        assert_eq_eps!(
172            core::f32::consts::E,
173            crate::f64::FloatExt::exp(1.0f32),
174            1e-6
175        );
176        assert_eq_eps!(7.389056098930649, crate::f64::FloatExt::exp(2.0f32), 1e-6);
177        assert_eq_eps!(
178            15.154261,
179            crate::f64::FloatExt::exp(core::f32::consts::E),
180            1e-6
181        );
182    }
183}