1 | //! `x^n` with fractional `n` approximation for a single-precision float. |
2 | |
3 | use super::F32; |
4 | |
5 | impl F32 { |
6 | /// Approximates a number raised to a floating point power. |
7 | pub fn powf(self, n: Self) -> Self { |
8 | // using x^n = exp(ln(x^n)) = exp(n*ln(x)) |
9 | if self >= Self::ZERO { |
10 | (n * self.ln()).exp() |
11 | } else if !n.is_integer() { |
12 | Self::NAN |
13 | } else if n.is_even() { |
14 | // if n is even, then we know that the result will have no sign, so we can remove it |
15 | (n * self.without_sign().ln()).exp() |
16 | } else { |
17 | // if n isn't even, we need to multiply by -1.0 at the end. |
18 | -(n * self.without_sign().ln()).exp() |
19 | } |
20 | } |
21 | } |
22 | |
23 | #[cfg (test)] |
24 | mod tests { |
25 | use super::F32; |
26 | |
27 | /// error builds up from both exp and ln approximation, so we double the error allowed. |
28 | pub(crate) const MAX_ERROR: f32 = 0.002; |
29 | |
30 | /// powf(3,x) test vectors - `(input, output)` |
31 | pub(crate) const TEST_VECTORS_POW3: &[(f32, f32)] = &[ |
32 | (-1e-20, 1.0), |
33 | (-1e-19, 1.0), |
34 | (-1e-18, 1.0), |
35 | (-1e-17, 1.0), |
36 | (-1e-16, 0.9999999999999999), |
37 | (-1e-15, 0.9999999999999989), |
38 | (-1e-14, 0.999999999999989), |
39 | (-1e-13, 0.9999999999998901), |
40 | (-1e-12, 0.9999999999989014), |
41 | (-1e-11, 0.9999999999890139), |
42 | (-1e-10, 0.9999999998901388), |
43 | (-1e-09, 0.9999999989013877), |
44 | (-1e-08, 0.9999999890138772), |
45 | (-1e-07, 0.999_999_9), |
46 | (-1e-06, 0.999_998_9), |
47 | (-1e-05, 0.999_989_03), |
48 | (-1e-04, 0.999_890_15), |
49 | (-0.001, 0.998_901_96), |
50 | (-0.01, 0.989_074), |
51 | (-0.1, 0.895_958_5), |
52 | (-1.0, 0.333_333_34), |
53 | (-10.0, 1.693_508_8e-5), |
54 | (-100.0, 0e0), |
55 | (-1000.0, 0.0), |
56 | (1e-20, 1.0), |
57 | (1e-19, 1.0), |
58 | (1e-18, 1.0), |
59 | (1e-17, 1.0), |
60 | (1e-16, 1.0), |
61 | (1e-15, 1.000000000000001), |
62 | (1e-14, 1.0000000000000109), |
63 | (1e-13, 1.00000000000011), |
64 | (1e-12, 1.0000000000010987), |
65 | (1e-11, 1.000000000010986), |
66 | (1e-10, 1.0000000001098612), |
67 | (1e-09, 1.0000000010986123), |
68 | (1e-08, 1.000000010986123), |
69 | (1e-07, 1.000_000_1), |
70 | (1e-06, 1.000_001_1), |
71 | (1e-05, 1.000_011), |
72 | (1e-04, 1.000_109_9), |
73 | (0.001, 1.001_099_2), |
74 | (0.01, 1.011_046_6), |
75 | (0.1, 1.116_123_2), |
76 | (1.0, 3.0), |
77 | (10.0, 59049.0), |
78 | ]; |
79 | |
80 | /// powf(150,x) test vectors - `(input, output)` |
81 | pub(crate) const TEST_VECTORS_POW150: &[(f32, f32)] = &[ |
82 | (-1e-20, 1.0), |
83 | (-1e-19, 1.0), |
84 | (-1e-18, 1.0), |
85 | (-1e-17, 1.0), |
86 | (-1e-16, 0.9999999999999994), |
87 | (-1e-15, 0.999999999999995), |
88 | (-1e-14, 0.9999999999999499), |
89 | (-1e-13, 0.999999999999499), |
90 | (-1e-12, 0.9999999999949893), |
91 | (-1e-11, 0.9999999999498936), |
92 | (-1e-10, 0.9999999994989365), |
93 | (-1e-09, 0.9999999949893649), |
94 | (-1e-08, 0.999_999_94), |
95 | (-1e-07, 0.999_999_5), |
96 | (-1e-06, 0.999_995), |
97 | (-1e-05, 0.999_949_9), |
98 | (-1e-04, 0.999_499_1), |
99 | (-0.001, 0.995_001_9), |
100 | (-0.01, 0.951_128_24), |
101 | (-0.1, 0.605_885_9), |
102 | (-1.0, 0.006_666_667), |
103 | (-10.0, 1.734_153e-22), |
104 | (-100.0, 0e0), |
105 | (-1000.0, 0.0), |
106 | (-10000.0, 0.0), |
107 | (-100000.0, 0.0), |
108 | (-1000000.0, 0.0), |
109 | (-10000000.0, 0.0), |
110 | (-100000000.0, 0.0), |
111 | (-1000000000.0, 0.0), |
112 | (-10000000000.0, 0.0), |
113 | (-100000000000.0, 0.0), |
114 | (-1000000000000.0, 0.0), |
115 | (-10000000000000.0, 0.0), |
116 | (-100000000000000.0, 0.0), |
117 | (-1000000000000000.0, 0.0), |
118 | (-1e+16, 0.0), |
119 | (-1e+17, 0.0), |
120 | (-1e+18, 0.0), |
121 | (-1e+19, 0.0), |
122 | (1e-20, 1.0), |
123 | (1e-19, 1.0), |
124 | (1e-18, 1.0), |
125 | (1e-17, 1.0), |
126 | (1e-16, 1.0000000000000004), |
127 | (1e-15, 1.000000000000005), |
128 | (1e-14, 1.0000000000000502), |
129 | (1e-13, 1.0000000000005012), |
130 | (1e-12, 1.0000000000050107), |
131 | (1e-11, 1.0000000000501064), |
132 | (1e-10, 1.0000000005010636), |
133 | (1e-09, 1.0000000050106352), |
134 | (1e-08, 1.000000050106354), |
135 | (1e-07, 1.000_000_5), |
136 | (1e-06, 1.000_005), |
137 | (1e-05, 1.000_050_1), |
138 | (1e-04, 1.000_501_2), |
139 | (0.001, 1.005_023_2), |
140 | (0.01, 1.051_382_9), |
141 | (0.1, 1.650_475_6), |
142 | (1.0, 150.0), |
143 | (10.0, 5.766_504e21), |
144 | ]; |
145 | |
146 | /// misc powf(x,n) test vectors - `(base_input, power_input, output)` |
147 | pub(crate) const TEST_VECTORS_MISC: &[(f32, f32, f32)] = &[ |
148 | (-0.5881598, 2.0, 0.345_931_95), |
149 | (-0.5881598, 3.2, f32::NAN), |
150 | (-0.5881598, 3.0, -0.203_463_27), |
151 | (-1000000.0, 4.0, 1e+24), |
152 | ]; |
153 | |
154 | fn calc_relative_error(experimental: F32, expected: f32) -> F32 { |
155 | if experimental.is_nan() && expected.is_nan() { |
156 | F32::ZERO |
157 | } else if expected != 0.0 { |
158 | (experimental - expected) / expected |
159 | } else { |
160 | (experimental - expected) / (expected + 1.0e-20) |
161 | } |
162 | } |
163 | |
164 | #[test ] |
165 | fn sanity_check() { |
166 | for &(x, expected) in TEST_VECTORS_POW3 { |
167 | let exp_x = F32(3.0).powf(F32(x)); |
168 | let relative_error = calc_relative_error(exp_x, expected); |
169 | |
170 | assert!( |
171 | relative_error <= MAX_ERROR, |
172 | "relative_error {} too large for input {} : {} vs {}" , |
173 | relative_error, |
174 | x, |
175 | exp_x, |
176 | expected |
177 | ); |
178 | } |
179 | |
180 | for &(x, expected) in TEST_VECTORS_POW150 { |
181 | let exp_x = F32(150.0).powf(F32(x)); |
182 | let relative_error = calc_relative_error(exp_x, expected); |
183 | |
184 | assert!( |
185 | relative_error <= MAX_ERROR, |
186 | "relative_error {} too large for input {} : {} vs {}" , |
187 | relative_error, |
188 | x, |
189 | exp_x, |
190 | expected |
191 | ); |
192 | } |
193 | |
194 | for &(base_input, power_input, expected) in TEST_VECTORS_MISC { |
195 | let exp_x = F32(base_input).powf(F32(power_input)); |
196 | let relative_error = calc_relative_error(exp_x, expected); |
197 | |
198 | assert!( |
199 | relative_error <= MAX_ERROR, |
200 | "relative_error {} too large for input {}.powf({}) : {} vs {}" , |
201 | relative_error, |
202 | base_input, |
203 | power_input, |
204 | exp_x, |
205 | expected |
206 | ); |
207 | } |
208 | } |
209 | } |
210 | |