1pub(crate) const LOG2E: f32 = std::f32::consts::LOG2_E;
19pub(crate) const LN2_HI: f32 = std::f32::consts::LN_2;
22pub(crate) const LN2_LO: f32 = -1.904_654_3e-9;
23pub(crate) const MAGIC: f32 = 12_582_912.0;
26pub(crate) const X_MAX: f32 = 89.0;
29pub(crate) const X_MIN: f32 = -104.0;
30pub(crate) const Q: [f32; 6] = [
33 0.5,
34 0.166_666_67,
35 0.041_666_463,
36 0.008_333_298,
37 0.001_393_368,
38 0.000_198_993_18,
39];
40
41#[inline(always)]
43fn pow2(n: i32) -> f32 {
44 f32::from_bits(((n + 127) as u32) << 23)
45}
46
47#[inline(always)]
50pub(crate) fn exp(x: f32) -> f32 {
51 let x = x.clamp(X_MIN, X_MAX);
53 let n_f = x.mul_add(LOG2E, MAGIC) - MAGIC;
54 let n = n_f as i32;
55 let r = (-n_f).mul_add(LN2_HI, x);
56 let r = (-n_f).mul_add(LN2_LO, r);
57 let q = Q[5]
58 .mul_add(r, Q[4])
59 .mul_add(r, Q[3])
60 .mul_add(r, Q[2])
61 .mul_add(r, Q[1])
62 .mul_add(r, Q[0]);
63 let tail = (r * r) * q;
64 let hi = 1.0 + r;
67 let lo = r - (hi - 1.0);
68 let e = hi + (lo + tail);
69 let half = n >> 1;
71 e * pow2(half) * pow2(n - half)
72}
73
74#[inline(always)]
78pub fn sigmoid(x: f32) -> f32 {
79 1.0 / (1.0 + exp(-x))
80}
81
82#[cfg(test)]
83mod tests {
84 use super::*;
85
86 fn ulps(a: f32, b: f32) -> u32 {
88 let key = |x: f32| {
89 let bits = x.to_bits() as i64;
90 if bits & 0x8000_0000 != 0 {
91 -(bits & 0x7fff_ffff)
92 } else {
93 bits
94 }
95 };
96 (key(a) - key(b)).unsigned_abs() as u32
97 }
98
99 fn nearly_exact(x: f32) -> f32 {
101 f64::from(x).exp() as f32
102 }
103
104 #[test]
105 fn exp_of_the_special_values() {
106 assert_eq!(exp(0.0), 1.0);
107 assert_eq!(exp(-0.0), 1.0);
108 assert_eq!(exp(f32::INFINITY), f32::INFINITY);
109 assert_eq!(exp(f32::NEG_INFINITY).to_bits(), 0);
110 assert!(exp(f32::NAN).is_nan());
111 assert!(exp(-f32::NAN).is_nan());
112 }
113
114 #[test]
115 fn exp_of_one_is_e() {
116 assert_eq!(exp(1.0), std::f32::consts::E);
117 assert_eq!(exp(-1.0), 1.0 / std::f32::consts::E);
118 }
119
120 #[test]
121 fn exp_overflows_exactly_where_the_result_leaves_f32() {
122 let threshold = f64::from(f32::MAX) * (1.0 + 2f64.powi(-24));
124 let edge = 88.722_84_f32.to_bits();
125 for bits in edge - 40..edge + 40 {
126 let x = f32::from_bits(bits);
127 let finite = f64::from(x).exp() < threshold;
128 assert_eq!(exp(x).is_finite(), finite, "x={x}");
129 if finite {
130 assert!(ulps(exp(x), nearly_exact(x)) <= 1, "x={x}");
131 }
132 }
133 assert_eq!(exp(100.0), f32::INFINITY);
134 assert_eq!(exp(1.0e30), f32::INFINITY);
135 }
136
137 #[test]
138 fn exp_underflows_through_the_subnormals_to_zero() {
139 let smallest = f32::from_bits(1);
140 assert_eq!(exp(-103.0), nearly_exact(-103.0));
142 assert_eq!(exp(-103.27), smallest);
143 assert_eq!(exp(-103.99).to_bits(), 0);
144 assert_eq!(exp(-104.0).to_bits(), 0);
145 assert_eq!(exp(-1.0e30).to_bits(), 0);
146 for k in 1..=40 {
147 let x = -87.0 - 0.4 * k as f32;
148 assert!(ulps(exp(x), nearly_exact(x)) <= 1, "x={x}");
149 }
150 }
151
152 #[test]
153 fn exp_is_within_one_ulp_of_the_exact_result() {
154 let mut state = 7u64;
155 for _ in 0..200_000 {
156 state = state
157 .wrapping_mul(6364136223846793005)
158 .wrapping_add(1442695040888963407);
159 let x = f32::from_bits((state >> 32) as u32);
160 if !x.is_finite() || !(-103.0..=88.0).contains(&x) {
161 continue;
162 }
163 assert!(ulps(exp(x), nearly_exact(x)) <= 1, "x={x}");
164 }
165 }
166
167 #[test]
168 fn exp_is_exact_for_tiny_arguments() {
169 for e in 26..60 {
171 let x = 2f32.powi(-e);
172 assert_eq!(exp(x), 1.0, "x=2^-{e}");
173 assert_eq!(exp(-x), 1.0, "x=-2^-{e}");
174 }
175 }
176
177 #[test]
178 fn sigmoid_of_the_special_values_and_the_edges() {
179 assert_eq!(sigmoid(0.0), 0.5);
180 assert_eq!(sigmoid(f32::INFINITY), 1.0);
181 assert_eq!(sigmoid(f32::NEG_INFINITY), 0.0);
182 assert!(sigmoid(f32::NAN).is_nan());
183 assert_eq!(sigmoid(20.0), 1.0);
184 assert_eq!(sigmoid(-88.8).to_bits(), 0);
187 assert_eq!(sigmoid(-100.0).to_bits(), 0);
188 assert_eq!(sigmoid(-1.0e30).to_bits(), 0);
189 }
190
191 #[test]
192 fn sigmoid_matches_the_f64_formula_within_two_ulp() {
193 for i in -2000..=2000 {
194 let x = i as f32 * 0.01;
195 let want = (1.0 / (1.0 + (-f64::from(x)).exp())) as f32;
196 assert!(ulps(sigmoid(x), want) <= 2, "x={x}");
197 }
198 }
199}