micromath/float/
log.rs

1//! log_b(a) approximation for a single-precision float.
2
3use super::F32;
4
5impl F32 {
6    /// Approximates the logarithm of the number with respect to an arbitrary base.
7    pub fn log(self, base: Self) -> Self {
8        (Self::ONE / base.ln()) * self.ln()
9    }
10}
11
12#[cfg(test)]
13mod tests {
14    use super::F32;
15
16    pub(crate) const MAX_ERROR: f32 = 0.001;
17
18    /// log3(x) test vectors - `(input, output)`
19    pub(crate) const TEST_VECTORS_BASE3: &[(f32, f32)] = &[
20        (1e-20, -41.918_064),
21        (1e-19, -39.822_16),
22        (1e-18, -37.726_26),
23        (1e-17, -35.630_356),
24        (1e-16, -33.534_454),
25        (1e-15, -31.438_549),
26        (1e-14, -29.342_646),
27        (1e-13, -27.246_744),
28        (1e-12, -25.150_839),
29        (1e-11, -23.054_935),
30        (1e-10, -20.959_032),
31        (1e-09, -18.863_13),
32        (1e-08, -16.767_227),
33        (1e-07, -14.671_323),
34        (1e-06, -12.575_419),
35        (1e-05, -10.479_516),
36        (1e-04, -8.383_614),
37        (0.001, -6.287_709_7),
38        (0.01, -4.191_807),
39        (0.1, -2.095_903_4),
40        (10.0, 2.095_903_4),
41        (100.0, 4.191_807),
42        (1000.0, 6.287_709_7),
43        (10000.0, 8.383_614),
44        (100000.0, 10.479_516),
45        (1000000.0, 12.575_419),
46        (10000000.0, 14.671_323),
47        (100000000.0, 16.767_227),
48        (1000000000.0, 18.863_13),
49        (10000000000.0, 20.959_032),
50        (100000000000.0, 23.054_935),
51        (1000000000000.0, 25.150_839),
52        (10000000000000.0, 27.246_744),
53        (100000000000000.0, 29.342_646),
54        (1000000000000000.0, 31.438_549),
55        (1e+16, 33.534_454),
56        (1e+17, 35.630_356),
57        (1e+18, 37.726_26),
58        (1e+19, 39.822_16),
59    ];
60
61    /// log5.5(x) test vectors - `(input, output)`
62    pub(crate) const TEST_VECTORS_BASE5_5: &[(f32, f32)] = &[
63        (1e-20, -27.013_786),
64        (1e-19, -25.663_097),
65        (1e-18, -24.312_408),
66        (1e-17, -22.961_72),
67        (1e-16, -21.611_03),
68        (1e-15, -20.260_34),
69        (1e-14, -18.909_65),
70        (1e-13, -17.558_962),
71        (1e-12, -16.208_273),
72        (1e-11, -14.857_583),
73        (1e-10, -13.506_893),
74        (1e-09, -12.156_204),
75        (1e-08, -10.805_515),
76        (1e-07, -9.454_825),
77        (1e-06, -8.104_136),
78        (1e-05, -6.753_446_6),
79        (1e-04, -5.402_757_6),
80        (0.001, -4.052_068),
81        (0.01, -2.701_378_8),
82        (0.1, -1.350_689_4),
83        (10.0, 1.350_689_4),
84        (100.0, 2.701_378_8),
85        (1000.0, 4.052_068),
86        (10000.0, 5.402_757_6),
87        (100000.0, 6.753_446_6),
88        (1000000.0, 8.104_136),
89        (10000000.0, 9.454_825),
90        (100000000.0, 10.805_515),
91        (1000000000.0, 12.156_204),
92        (10000000000.0, 13.506_893),
93        (100000000000.0, 14.857_583),
94        (1000000000000.0, 16.208_273),
95        (10000000000000.0, 17.558_962),
96        (100000000000000.0, 18.909_65),
97        (1000000000000000.0, 20.260_34),
98        (1e+16, 21.611_03),
99        (1e+17, 22.961_72),
100        (1e+18, 24.312_408),
101        (1e+19, 25.663_097),
102    ];
103
104    /// log12.7(x) test vectors - `(input, output)`
105    pub(crate) const TEST_VECTORS_BASE12_7: &[(f32, f32)] = &[
106        (1e-20, -18.119_164),
107        (1e-19, -17.213_205),
108        (1e-18, -16.307_247),
109        (1e-17, -15.401_289),
110        (1e-16, -14.495_331),
111        (1e-15, -13.589_373),
112        (1e-14, -12.683_414),
113        (1e-13, -11.777_456),
114        (1e-12, -10.871_498),
115        (1e-11, -9.965_54),
116        (1e-10, -9.059_582),
117        (1e-09, -8.153_624),
118        (1e-08, -7.247_665_4),
119        (1e-07, -6.341_707),
120        (1e-06, -5.435_749),
121        (1e-05, -4.529_791),
122        (1e-04, -3.623_832_7),
123        (0.001, -2.717_874_5),
124        (0.01, -1.811_916_4),
125        (0.1, -0.905_958_2),
126        (10.0, 0.905_958_2),
127        (100.0, 1.811_916_4),
128        (1000.0, 2.717_874_5),
129        (10000.0, 3.623_832_7),
130        (100000.0, 4.529_791),
131        (1000000.0, 5.435_749),
132        (10000000.0, 6.341_707),
133        (100000000.0, 7.247_665_4),
134        (1000000000.0, 8.153_624),
135        (10000000000.0, 9.059_582),
136        (100000000000.0, 9.965_54),
137        (1000000000000.0, 10.871_498),
138        (10000000000000.0, 11.777_456),
139        (100000000000000.0, 12.683_414),
140        (1000000000000000.0, 13.589_373),
141        (1e+16, 14.495_331),
142        (1e+17, 15.401_289),
143        (1e+18, 16.307_247),
144        (1e+19, 17.213_205),
145    ];
146
147    #[test]
148    fn sanity_check() {
149        assert_eq!(F32::ONE.log(F32(3.0)), F32::ZERO);
150        assert_eq!(F32::ONE.log(F32(5.5)), F32::ZERO);
151        assert_eq!(F32::ONE.log(F32(12.7)), F32::ZERO);
152
153        for &(x, expected) in TEST_VECTORS_BASE3 {
154            let log_x = F32(x).log(F32(3.0));
155            let relative_error = (log_x - expected).abs() / expected;
156
157            assert!(
158                relative_error <= MAX_ERROR,
159                "relative_error {} too large: {} vs {}",
160                relative_error,
161                log_x,
162                expected
163            );
164        }
165
166        for &(x, expected) in TEST_VECTORS_BASE5_5 {
167            let log_x = F32(x).log(F32(5.5));
168            let relative_error = (log_x - expected).abs() / expected;
169
170            assert!(
171                relative_error <= MAX_ERROR,
172                "relative_error {} too large: {} vs {}",
173                relative_error,
174                log_x,
175                expected
176            );
177        }
178
179        for &(x, expected) in TEST_VECTORS_BASE12_7 {
180            let log_x = F32(x).log(F32(12.7));
181            let relative_error = (log_x - expected).abs() / expected;
182
183            assert!(
184                relative_error <= MAX_ERROR,
185                "relative_error {} too large: {} vs {}",
186                relative_error,
187                log_x,
188                expected
189            );
190        }
191    }
192}