1use core::f32;
2use core::f32::consts as f;
3use float;
4use ieee754::Ieee754;
5
6#[derive(Clone, Copy)]
7enum Base {
8 E,
9 Two,
10}
11impl Base {
12 #[inline(always)]
13 fn log2(self) -> f32 {
14 match self {
15 Base::E => f::LOG2_E,
16 Base::Two => 1.0,
17 }
18 }
19
20 #[inline(always)]
21 fn upper_limit(self) -> f32 {
22 128.0 / self.log2()
23 }
24
25 #[inline(always)]
26 fn lower_limit(self) -> f32 {
27 -127.0 / self.log2()
28 }
29}
30
31#[inline(always)]
32fn exp_raw_impl(x: f32, base: Base) -> f32 {
33 const A: f32 = (1 << float::SIGNIF) as f32;
34 const MASK: i32 = 0xff800000u32 as i32;
35 const EXP2_23: f32 = 1.1920929e-7;
36 const C0: f32 = 0.3371894346 * EXP2_23 * EXP2_23;
37 const C1: f32 = 0.657636276 * EXP2_23;
38 const C2: f32 = 1.00172476;
39
40 let a = A * base.log2();
41 let mul = (a * x) as i32;
42 let floor = mul & MASK;
43 let frac = (mul - floor) as f32;
44
45 let approx = (C0 * frac + C1) * frac + C2;
46 f32::from_bits(approx.bits().wrapping_add(floor as u32))
47}
48
49#[inline(always)]
50fn exp_impl(x: f32, base: Base) -> f32 {
51 if x <= base.lower_limit() {
52 0.0
53 } else if x < base.upper_limit() {
54 exp_raw_impl(x, base)
55 } else {
56 x + f32::INFINITY
59 }
60}
61
62#[inline]
73pub fn exp2_raw(x: f32) -> f32 {
74 exp_raw_impl(x, Base::Two)
75}
76
77#[inline]
88pub fn exp2(x: f32) -> f32 {
89 exp_impl(x, Base::Two)
90}
91
92#[inline]
104pub fn exp_raw(x: f32) -> f32 {
105 exp_raw_impl(x, Base::E)
106}
107
108#[inline]
120pub fn exp(x: f32) -> f32 {
121 exp_impl(x, Base::E)
122}
123
124#[cfg(test)]
125mod tests {
126 use super::*;
127 use std::{f32, num};
128
129 const PREC: u32 = 1 << 19;
130
131 #[test]
132 fn exp_rel_err_exhaustive() {
133 let mut max = 0.0;
134 for i in 0..PREC + 1 {
135 for j in -5..6 {
136 for &sign in &[-1.0, 1.0] {
137 let x = sign * (1.0 + i as f32 / PREC as f32) * 2f32.powi(j * 2);
138 let e = exp(x);
139 let t = x.exp();
140 let rel = e.rel_error(t).abs();
141
142 if t.classify() == num::FpCategory::Subnormal {
143 assert!(rel <= 1.0,
145 "{:.8}: e = {:.8e}, t = {:.8e}. {:.4}", x, e, t, rel); } else {
146 if rel > max { max = rel }
147 assert!(rel <= 0.002,
149 "{:.8}: e = {:.8e}, t = {:.8e}. {:.4}", x, e, t, rel);
150 }
151 }
152 }
153 }
154 println!("maximum {}", max);
155 }
156
157 #[test]
158 fn exp2_rel_err_exhaustive() {
159 let mut max = 0.0;
160 for i in 0..PREC + 1 {
161 for j in -5..6 {
162 for &sign in &[-1.0, 1.0] {
163 let x = sign * (1.0 + i as f32 / PREC as f32) * 2f32.powi(j * 2);
164 let e = exp2(x);
165 let t = x.exp2();
166 let rel = e.rel_error(t).abs();
167 if t.classify() == num::FpCategory::Subnormal {
168 assert!(rel <= 1.0,
170 "{:.8}: e = {:.8e}, t = {:.8e}. {:.4}", x, e, t, rel); } else {
171 if rel > max { max = rel }
172 assert!(rel <= 0.002,
174 "{:.8}: e = {:.8e}, t = {:.8e}. {:.4}", x, e, t, rel);
175 }
176 }
177 }
178 }
179 println!("maximum {}", max);
180 }
181
182 #[test]
183 fn exp_edge_cases() {
184 assert!(exp(f32::NAN).is_nan());
185 assert_eq!(exp(f32::NEG_INFINITY), 0.0);
186 assert!((exp(0.0) - 1.0).abs() < 0.002);
187 assert_eq!(exp(f32::INFINITY), f32::INFINITY);
188 }
189
190 #[test]
191 fn exp2_edge_cases() {
192 assert!(exp2(f32::NAN).is_nan());
193 assert_eq!(exp2(f32::NEG_INFINITY), 0.0);
194 assert!((exp2(0.0) - 1.0).abs() < 0.002);
195 assert_eq!(exp2(f32::INFINITY), f32::INFINITY);
196 }
197}