/rust/registry/src/index.crates.io-1949cf8c6b5b557f/pxfm-0.1.30/src/exponents/exp2f.rs
Line | Count | Source |
1 | | /* |
2 | | * // Copyright (c) Radzivon Bartoshyk 7/2025. All rights reserved. |
3 | | * // |
4 | | * // Redistribution and use in source and binary forms, with or without modification, |
5 | | * // are permitted provided that the following conditions are met: |
6 | | * // |
7 | | * // 1. Redistributions of source code must retain the above copyright notice, this |
8 | | * // list of conditions and the following disclaimer. |
9 | | * // |
10 | | * // 2. Redistributions in binary form must reproduce the above copyright notice, |
11 | | * // this list of conditions and the following disclaimer in the documentation |
12 | | * // and/or other materials provided with the distribution. |
13 | | * // |
14 | | * // 3. Neither the name of the copyright holder nor the names of its |
15 | | * // contributors may be used to endorse or promote products derived from |
16 | | * // this software without specific prior written permission. |
17 | | * // |
18 | | * // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
19 | | * // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
20 | | * // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
21 | | * // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE |
22 | | * // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
23 | | * // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
24 | | * // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
25 | | * // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
26 | | * // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
27 | | * // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
28 | | */ |
29 | | use crate::common::{f_fmla, f_fmlaf, pow2if}; |
30 | | use crate::exponents::expf::{ExpfBackend, GenericExpfBackend}; |
31 | | use std::hint::black_box; |
32 | | |
33 | | const TBLSIZE: usize = 64; |
34 | | |
35 | | #[repr(align(64))] |
36 | | struct Exp2Table([(u32, u32); TBLSIZE]); |
37 | | |
38 | | #[rustfmt::skip] |
39 | | static EXP2FT: Exp2Table = Exp2Table([(0x3F3504F3, 0xB2D4175E),(0x3F36FD92, 0x3268D5EF),(0x3F38FBAF, 0xB30E8719),(0x3F3AFF5B, 0x3319E7DA),(0x3F3D08A4, 0x333CD82F),(0x3F3F179A, 0x330E1902),(0x3F412C4D, 0x32CCF4D7),(0x3F4346CD, 0x328F330E),(0x3F45672A, 0xB201B5B7),(0x3F478D75, 0x32CCCE34),(0x3F49B9BE, 0x335E937C),(0x3F4BEC15, 0x2FF41909),(0x3F4E248C, 0xB21760EA),(0x3F506334, 0x3283628B),(0x3F52A81E, 0x3340F500),(0x3F54F35B, 0x331202BD),(0x3F5744FD, 0x32B66A3E),(0x3F599D16, 0x32D0D9B1),(0x3F5BFBB8, 0x332ED93F),(0x3F5E60F5, 0x3350A709),(0x3F60CCDF, 0x32025744),(0x3F633F89, 0xB33A7C4D),(0x3F65B907, 0x321DA4E9),(0x3F68396A, 0xB2FF36A7),(0x3F6AC0C7, 0x3217E40E),(0x3F6D4F30, 0xB2400CBB),(0x3F6FE4BA, 0x331A2ACC),(0x3F728177, 0xB2B7D3E5),(0x3F75257D, 0xB1FED2BE),(0x3F77D0DF, 0xB32B73BA),(0x3F7A83B3, 0x32579081),(0x3F7D3E0C, 0xB19726B5),(0x3F800000, 0x00000000),(0x3F8164D2, 0x320C09FB),(0x3F82CD87, 0x3391E031),(0x3F843A29, 0x33287EEF),(0x3F85AAC3, 0xB38F6665),(0x3F871F62, 0x339004AB),(0x3F88980F, 0x33AC4561),(0x3F8A14D5, 0xB39CDAEA),(0x3F8B95C2, 0x32949D5C),(0x3F8D1ADF, 0xB36F79FA),(0x3F8EA43A, 0x33971DC2),(0x3F9031DC, 0xB32BD022),(0x3F91C3D3, 0xB3928952),(0x3F935A2B, 0xB2EBFECF),(0x3F94F4F0, 0x3357B8BB),(0x3F96942D, 0xB307353B),(0x3F9837F0, 0xB345DFE9),(0x3F99E046, 0x3382A804),(0x3F9B8D3A, 0x3326993E),(0x3F9D3EDA, 0x3350A029),(0x3F9EF532, 0xB3605F62),(0x3FA0B051, 0xB210909B),(0x3FA27043, 0xB0DDC369),(0x3FA43516, 0x33385844),(0x3FA5FED7, 0x33400757),(0x3FA7CD94, 0x3325446E),(0x3FA9A15B, 0x33237A50),(0x3FAB7A3A, 0x33201CA4),(0x3FAD583F, 0x32394687),(0x3FAF3B79, 0x332E1225),(0x3FB123F6, 0x33838969),(0x3FB311C4, 0xB219F2BA)]); |
40 | | |
41 | | /** |
42 | | Generated by SageMath: |
43 | | ```python |
44 | | print("[") |
45 | | for k in range(64): |
46 | | k = RealField(150)(2)**(RealField(150)(k) / RealField(150)(64)) |
47 | | print(double_to_hex(k) + ",") |
48 | | print("];") |
49 | | ``` |
50 | | **/ |
51 | | pub(crate) static EXP2F_TABLE: [u64; 64] = [ |
52 | | 0x3ff0000000000000, |
53 | | 0x3ff02c9a3e778061, |
54 | | 0x3ff059b0d3158574, |
55 | | 0x3ff0874518759bc8, |
56 | | 0x3ff0b5586cf9890f, |
57 | | 0x3ff0e3ec32d3d1a2, |
58 | | 0x3ff11301d0125b51, |
59 | | 0x3ff1429aaea92de0, |
60 | | 0x3ff172b83c7d517b, |
61 | | 0x3ff1a35beb6fcb75, |
62 | | 0x3ff1d4873168b9aa, |
63 | | 0x3ff2063b88628cd6, |
64 | | 0x3ff2387a6e756238, |
65 | | 0x3ff26b4565e27cdd, |
66 | | 0x3ff29e9df51fdee1, |
67 | | 0x3ff2d285a6e4030b, |
68 | | 0x3ff306fe0a31b715, |
69 | | 0x3ff33c08b26416ff, |
70 | | 0x3ff371a7373aa9cb, |
71 | | 0x3ff3a7db34e59ff7, |
72 | | 0x3ff3dea64c123422, |
73 | | 0x3ff4160a21f72e2a, |
74 | | 0x3ff44e086061892d, |
75 | | 0x3ff486a2b5c13cd0, |
76 | | 0x3ff4bfdad5362a27, |
77 | | 0x3ff4f9b2769d2ca7, |
78 | | 0x3ff5342b569d4f82, |
79 | | 0x3ff56f4736b527da, |
80 | | 0x3ff5ab07dd485429, |
81 | | 0x3ff5e76f15ad2148, |
82 | | 0x3ff6247eb03a5585, |
83 | | 0x3ff6623882552225, |
84 | | 0x3ff6a09e667f3bcd, |
85 | | 0x3ff6dfb23c651a2f, |
86 | | 0x3ff71f75e8ec5f74, |
87 | | 0x3ff75feb564267c9, |
88 | | 0x3ff7a11473eb0187, |
89 | | 0x3ff7e2f336cf4e62, |
90 | | 0x3ff82589994cce13, |
91 | | 0x3ff868d99b4492ed, |
92 | | 0x3ff8ace5422aa0db, |
93 | | 0x3ff8f1ae99157736, |
94 | | 0x3ff93737b0cdc5e5, |
95 | | 0x3ff97d829fde4e50, |
96 | | 0x3ff9c49182a3f090, |
97 | | 0x3ffa0c667b5de565, |
98 | | 0x3ffa5503b23e255d, |
99 | | 0x3ffa9e6b5579fdbf, |
100 | | 0x3ffae89f995ad3ad, |
101 | | 0x3ffb33a2b84f15fb, |
102 | | 0x3ffb7f76f2fb5e47, |
103 | | 0x3ffbcc1e904bc1d2, |
104 | | 0x3ffc199bdd85529c, |
105 | | 0x3ffc67f12e57d14b, |
106 | | 0x3ffcb720dcef9069, |
107 | | 0x3ffd072d4a07897c, |
108 | | 0x3ffd5818dcfba487, |
109 | | 0x3ffda9e603db3285, |
110 | | 0x3ffdfc97337b9b5f, |
111 | | 0x3ffe502ee78b3ff6, |
112 | | 0x3ffea4afa2a490da, |
113 | | 0x3ffefa1bee615a27, |
114 | | 0x3fff50765b6e4540, |
115 | | 0x3fffa7c1819e90d8, |
116 | | ]; |
117 | | |
118 | | /* ULP 0.508 method |
119 | | let redux = f32::from_bits(0x4b400000) / TBLSIZE as f32; |
120 | | |
121 | | let ui = f32::to_bits(d + redux); |
122 | | let mut i0 = ui; |
123 | | i0 = i0.wrapping_add(TBLSIZE as u32 / 2); |
124 | | let k = i0 / TBLSIZE as u32; |
125 | | i0 &= TBLSIZE as u32 - 1; |
126 | | let mut uf = f32::from_bits(ui); |
127 | | uf -= redux; |
128 | | |
129 | | let item = EXP2FT.0[i0 as usize]; |
130 | | let z0: f32 = f32::from_bits(item.0); |
131 | | let z1: f32 = f32::from_bits(item.1); |
132 | | |
133 | | let f: f32 = d - uf - z1; |
134 | | |
135 | | let mut u = 0.055504108664458832; |
136 | | u = f_fmlaf(u, f, 0.24022650695908768); |
137 | | u = f_fmlaf(u, f, 0.69314718055994973); |
138 | | u *= f; |
139 | | |
140 | | let i2 = pow2if(k as i32); |
141 | | f_fmlaf(u, z0, z0) * i2 |
142 | | */ |
143 | | |
144 | | #[inline(always)] |
145 | 0 | fn exp2f_gen<B: ExpfBackend>(x: f32, backend: B) -> f32 { |
146 | 0 | let mut t = x.to_bits(); |
147 | | |
148 | 0 | if (t & 0xffff) == 0 { |
149 | | // x maybe integer |
150 | 0 | let k: i32 = (((t >> 23) & 0xff) as i32).wrapping_sub(127); // 2^k <= |x| < 2^(k+1) |
151 | 0 | if k >= 0 && k < 9 && (t << (9i32.wrapping_add(k))) == 0 { |
152 | | // x integer, with 1 <= |x| < 2^9 |
153 | 0 | let msk = (t as i32) >> 31; |
154 | 0 | let mut m: i32 = (((t & 0x7fffff) | (1 << 23)) >> (23 - k)) as i32; |
155 | 0 | m = (m ^ msk).wrapping_sub(msk).wrapping_add(127); |
156 | 0 | if m > 0 && m < 255 { |
157 | 0 | t = (m as u32).wrapping_shl(23); |
158 | 0 | return f32::from_bits(t); |
159 | 0 | } else if m <= 0 && m > -23 { |
160 | 0 | t = 1i32.wrapping_shl(22i32.wrapping_add(m) as u32) as u32; |
161 | 0 | return f32::from_bits(t); |
162 | 0 | } |
163 | 0 | } |
164 | 0 | } |
165 | 0 | let ux = t.wrapping_shl(1); |
166 | | |
167 | 0 | if ux >= 0x86000000u32 || ux < 0x65000000u32 { |
168 | | // |x| >= 128 or x=nan or |x| < 0x1p-26 |
169 | 0 | if ux < 0x65000000u32 { |
170 | 0 | return 1.0 + x; |
171 | 0 | } // |x| < 0x1p-26 |
172 | | // if x < -149 or 128 <= x is special |
173 | 0 | if !(t >= 0xc3000000u32 && t < 0xc3150000u32) { |
174 | 0 | if ux >= 0xffu32 << 24 { |
175 | | // x is inf or nan |
176 | 0 | if ux > (0xffu32 << 24) { |
177 | 0 | return x + x; |
178 | 0 | } // x = nan |
179 | | static IR: [f32; 2] = [f32::INFINITY, 0.]; |
180 | 0 | return IR[(t >> 31) as usize]; // x = +-inf |
181 | 0 | } |
182 | 0 | if t >= 0xc3150000u32 { |
183 | | // x < -149 |
184 | 0 | let z = x; |
185 | 0 | let mut y = f_fmla( |
186 | 0 | z as f64 + 149., |
187 | 0 | f64::from_bits(0x3690000000000000), |
188 | 0 | f64::from_bits(0x36a0000000000000), |
189 | | ); |
190 | 0 | y = y.max(f64::from_bits(0x3680000000000000)); |
191 | 0 | return y as f32; |
192 | 0 | } |
193 | | // now x >= 128 |
194 | 0 | let r = black_box(f64::from_bits(0x47e0000000000000)) |
195 | 0 | * black_box(f64::from_bits(0x47e0000000000000)); |
196 | 0 | return r as f32; |
197 | 0 | } |
198 | 0 | } |
199 | | |
200 | 0 | if ux <= 0x7a000000u32 { |
201 | | // |x| < 1/32 |
202 | | |
203 | | // Generated by Sollya exp2 on range [-1/32;1/32]: |
204 | | // d = [-1/32, 1/32]; |
205 | | // f_exp2f = (2^y - 1)/y; |
206 | | // Q = fpminimax(f_exp2f, 5, [|D...|], d, relative, floating); |
207 | | |
208 | | // See ./notes/exp2f_small.sollya |
209 | | const C: [u64; 6] = [ |
210 | | 0x3fe62e42fefa39f3, |
211 | | 0x3fcebfbdff82c57b, |
212 | | 0x3fac6b08d6f2d7aa, |
213 | | 0x3f83b2ab6fc92f5d, |
214 | | 0x3f55d897cfe27125, |
215 | | 0x3f243090e61e6af1, |
216 | | ]; |
217 | 0 | let xd = x as f64; |
218 | 0 | let p = backend.polyeval6( |
219 | 0 | xd, |
220 | 0 | f64::from_bits(C[0]), |
221 | 0 | f64::from_bits(C[1]), |
222 | 0 | f64::from_bits(C[2]), |
223 | 0 | f64::from_bits(C[3]), |
224 | 0 | f64::from_bits(C[4]), |
225 | 0 | f64::from_bits(C[5]), |
226 | | ); |
227 | 0 | return backend.fma(p, xd, 1.) as f32; |
228 | 0 | } |
229 | | |
230 | 0 | let x_d = x as f64; |
231 | 0 | let kf = backend.round(x_d * 64.); |
232 | 0 | let k = unsafe { kf.to_int_unchecked::<i32>() }; // it's already not indeterminate. |
233 | | // dx = lo = x - (hi + mid) = x - kf * 2^(-6) |
234 | 0 | let dx = backend.fma(f64::from_bits(0xbf90000000000000), kf, x_d); |
235 | | |
236 | | const TABLE_BITS: u32 = 6; |
237 | | const TABLE_MASK: u64 = (1u64 << TABLE_BITS) - 1; |
238 | | |
239 | | // hi = floor(kf * 2^(-5)) |
240 | | // exp_hi = shift hi to the exponent field of double precision. |
241 | 0 | let exp_hi: i64 = ((k >> TABLE_BITS) as i64).wrapping_shl(52); |
242 | | |
243 | | // mh = 2^hi * 2^mid |
244 | | // mh_bits = bit field of mh |
245 | 0 | let mh_bits = (EXP2F_TABLE[((k as u64) & TABLE_MASK) as usize] as i64).wrapping_add(exp_hi); |
246 | 0 | let mh = f64::from_bits(mh_bits as u64); |
247 | | |
248 | | // Degree-4 polynomial approximating (2^x - 1)/x generated by Sollya with: |
249 | | // > P = fpminimax((2^y - 1)/y, 4, [|D...|], [-1/64. 1/64]); |
250 | | // see ./notes/exp2f.sollya |
251 | | const C: [u64; 5] = [ |
252 | | 0x3fe62e42fefa39ef, |
253 | | 0x3fcebfbdff8131c4, |
254 | | 0x3fac6b08d7061695, |
255 | | 0x3f83b2b1bee74b2a, |
256 | | 0x3f55d88091198529, |
257 | | ]; |
258 | 0 | let dx_sq = dx * dx; |
259 | 0 | let c1 = backend.fma(dx, f64::from_bits(C[0]), 1.0); |
260 | 0 | let c2 = backend.fma(dx, f64::from_bits(C[2]), f64::from_bits(C[1])); |
261 | 0 | let c3 = backend.fma(dx, f64::from_bits(C[4]), f64::from_bits(C[3])); |
262 | 0 | let p = backend.fma(dx_sq, c3, c2); |
263 | | // 2^x = 2^(hi + mid + lo) |
264 | | // = 2^(hi + mid) * 2^lo |
265 | | // ~ mh * (1 + lo * P(lo)) |
266 | | // = mh + (mh*lo) * P(lo) |
267 | 0 | backend.fma(p, dx_sq * mh, c1 * mh) as f32 |
268 | 0 | } Unexecuted instantiation: pxfm::exponents::exp2f::exp2f_gen::<pxfm::exponents::expf::FmaBackend> Unexecuted instantiation: pxfm::exponents::exp2f::exp2f_gen::<pxfm::exponents::expf::GenericExpfBackend> |
269 | | |
270 | | #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] |
271 | | #[target_feature(enable = "avx", enable = "fma")] |
272 | 0 | unsafe fn exp2f_fma_impl(x: f32) -> f32 { |
273 | | use crate::exponents::expf::FmaBackend; |
274 | 0 | exp2f_gen(x, FmaBackend {}) |
275 | 0 | } |
276 | | |
277 | | /// Computing exp2f |
278 | | /// |
279 | | /// ULP 0.4999994 |
280 | | #[inline] |
281 | 0 | pub fn f_exp2f(x: f32) -> f32 { |
282 | | #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] |
283 | | { |
284 | | exp2f_gen(x, GenericExpfBackend {}) |
285 | | } |
286 | | #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] |
287 | | { |
288 | | use std::sync::OnceLock; |
289 | | static EXECUTOR: OnceLock<unsafe fn(f32) -> f32> = OnceLock::new(); |
290 | 0 | let q = EXECUTOR.get_or_init(|| { |
291 | 0 | if std::arch::is_x86_feature_detected!("avx") |
292 | 0 | && std::arch::is_x86_feature_detected!("fma") |
293 | | { |
294 | 0 | exp2f_fma_impl |
295 | | } else { |
296 | 0 | fn def_exp2f(x: f32) -> f32 { |
297 | 0 | exp2f_gen(x, GenericExpfBackend {}) |
298 | 0 | } |
299 | 0 | def_exp2f |
300 | | } |
301 | 0 | }); |
302 | 0 | unsafe { q(x) } |
303 | | } |
304 | 0 | } |
305 | | |
306 | | #[inline] |
307 | 0 | pub(crate) fn dirty_exp2f(d: f32) -> f32 { |
308 | 0 | let redux = f32::from_bits(0x4b400000) / TBLSIZE as f32; |
309 | | |
310 | 0 | let ui = f32::to_bits(d + redux); |
311 | 0 | let mut i0 = ui; |
312 | 0 | i0 = i0.wrapping_add(TBLSIZE as u32 / 2); |
313 | 0 | let k = i0 / TBLSIZE as u32; |
314 | 0 | i0 &= TBLSIZE as u32 - 1; |
315 | 0 | let mut uf = f32::from_bits(ui); |
316 | 0 | uf -= redux; |
317 | | |
318 | 0 | let item = EXP2FT.0[i0 as usize]; |
319 | 0 | let z0: f32 = f32::from_bits(item.0); |
320 | | |
321 | 0 | let f: f32 = d - uf; |
322 | | |
323 | 0 | let mut u = 0.24022650695908768; |
324 | 0 | u = f_fmlaf(u, f, 0.69314718055994973); |
325 | 0 | u *= f; |
326 | | |
327 | 0 | let i2 = pow2if(k as i32); |
328 | 0 | f_fmlaf(u, z0, z0) * i2 |
329 | 0 | } Unexecuted instantiation: pxfm::exponents::exp2f::dirty_exp2f Unexecuted instantiation: pxfm::exponents::exp2f::dirty_exp2f |
330 | | |
331 | | #[cfg(test)] |
332 | | mod tests { |
333 | | use super::*; |
334 | | |
335 | | #[test] |
336 | | fn test_exp2f() { |
337 | | assert!(f_exp2f(f32::from_bits(0x7fc0_0000)).is_nan()); |
338 | | assert_eq!(f_exp2f(1. / 64.), 1.0108893); |
339 | | assert_eq!(f_exp2f(2.0), 4.0); |
340 | | assert_eq!(f_exp2f(3.0), 8.0); |
341 | | assert_eq!(f_exp2f(4.0), 16.0); |
342 | | assert_eq!(f_exp2f(10.0), 1024.0); |
343 | | assert_eq!(f_exp2f(-10.0), 0.0009765625); |
344 | | assert!(f_exp2f(f32::NAN).is_nan()); |
345 | | assert_eq!(f_exp2f(-0.35), 0.7845841); |
346 | | assert_eq!(f_exp2f(0.35), 1.2745606); |
347 | | assert!(f_exp2f(f32::INFINITY).is_infinite()); |
348 | | assert_eq!(f_exp2f(f32::NEG_INFINITY), 0.0); |
349 | | } |
350 | | |
351 | | #[test] |
352 | | fn test_dirty_exp2f() { |
353 | | assert!((dirty_exp2f(0.35f32) - 0.35f32.exp2()).abs() < 1e-5); |
354 | | assert!((dirty_exp2f(-0.6f32) - (-0.6f32).exp2()).abs() < 1e-5); |
355 | | } |
356 | | } |