/rust/registry/src/index.crates.io-1949cf8c6b5b557f/pxfm-0.1.25/src/exponents/exp2m1f.rs
Line | Count | Source |
1 | | /* |
2 | | * // Copyright (c) Radzivon Bartoshyk 6/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; |
30 | | use crate::exponents::exp2f::EXP2F_TABLE; |
31 | | use crate::polyeval::f_polyeval3; |
32 | | use crate::rounding::CpuRound; |
33 | | |
34 | | /// Computes 2^x - 1 |
35 | | /// |
36 | | /// Max found ULP 0.5 |
37 | | #[inline] |
38 | 0 | pub fn f_exp2m1f(x: f32) -> f32 { |
39 | 0 | let x_u = x.to_bits(); |
40 | 0 | let x_abs = x_u & 0x7fff_ffffu32; |
41 | 0 | if x_abs >= 0x4300_0000u32 || x_abs <= 0x3d00_0000u32 { |
42 | | // |x| <= 2^-5 |
43 | 0 | if x_abs <= 0x3d00_0000u32 { |
44 | | // Minimax polynomial generated by Sollya with: |
45 | | // > display = hexadecimal; |
46 | | // > fpminimax((2^x - 1)/x, 5, [|D...|], [-2^-5, 2^-5]); |
47 | | const C: [u64; 6] = [ |
48 | | 0x3fe62e42fefa39f3, |
49 | | 0x3fcebfbdff82c57b, |
50 | | 0x3fac6b08d6f2d7aa, |
51 | | 0x3f83b2ab6fc92f5d, |
52 | | 0x3f55d897cfe27125, |
53 | | 0x3f243090e61e6af1, |
54 | | ]; |
55 | 0 | let xd = x as f64; |
56 | 0 | let xsq = xd * xd; |
57 | 0 | let c0 = f_fmla(xd, f64::from_bits(C[1]), f64::from_bits(C[0])); |
58 | 0 | let c1 = f_fmla(xd, f64::from_bits(C[3]), f64::from_bits(C[2])); |
59 | 0 | let c2 = f_fmla(xd, f64::from_bits(C[5]), f64::from_bits(C[4])); |
60 | 0 | let p = f_polyeval3(xsq, c0, c1, c2); |
61 | 0 | return (p * xd) as f32; |
62 | 0 | } |
63 | | |
64 | | // x >= 128, or x is nan |
65 | 0 | if x.is_sign_positive() { |
66 | | // x >= 128 and 2^x - 1 rounds to +inf, or x is +inf or nan |
67 | 0 | return x + f32::INFINITY; |
68 | 0 | } |
69 | 0 | } |
70 | | |
71 | 0 | if x <= -25.0 { |
72 | | // 2^(-inf) - 1 = -1 |
73 | 0 | if x.is_infinite() { |
74 | 0 | return -1.0; |
75 | 0 | } |
76 | | // 2^nan - 1 = nan |
77 | 0 | if x.is_nan() { |
78 | 0 | return x; |
79 | 0 | } |
80 | 0 | return -1.0; |
81 | 0 | } |
82 | | |
83 | | // For -25 < x < 128, to compute 2^x, we perform the following range |
84 | | // reduction: find hi, mid, lo such that: |
85 | | // x = hi + mid + lo, in which: |
86 | | // hi is an integer, |
87 | | // 0 <= mid * 2^5 < 32 is an integer, |
88 | | // -2^(-6) <= lo <= 2^(-6). |
89 | | // In particular, |
90 | | // hi + mid = round(x * 2^5) * 2^(-5). |
91 | | // Then, |
92 | | // 2^x = 2^(hi + mid + lo) = 2^hi * 2^mid * 2^lo. |
93 | | // 2^mid is stored in the lookup table of 32 elements. |
94 | | // 2^lo is computed using a degree-4 minimax polynomial generated by Sollya. |
95 | | // We perform 2^hi * 2^mid by simply add hi to the exponent field of 2^mid. |
96 | | |
97 | | // kf = (hi + mid) * 2^5 = round(x * 2^5) |
98 | | |
99 | 0 | let xd = x as f64; |
100 | | |
101 | 0 | let kf = (x * 64.0).cpu_round(); |
102 | 0 | let k = unsafe { kf.to_int_unchecked::<i32>() }; // it's already not indeterminate. |
103 | | // dx = lo = x - (hi + mid) = x - kf * 2^(-6) |
104 | 0 | let dx = f_fmla(f64::from_bits(0xbf90000000000000), kf as f64, xd); |
105 | | |
106 | | const TABLE_BITS: u32 = 6; |
107 | | const TABLE_MASK: u64 = (1u64 << TABLE_BITS) - 1; |
108 | | |
109 | | // hi = floor(kf * 2^(-5)) |
110 | | // exp_hi = shift hi to the exponent field of double precision. |
111 | 0 | let exp_hi: i64 = ((k >> TABLE_BITS) as i64).wrapping_shl(52); |
112 | | |
113 | | // mh = 2^hi * 2^mid |
114 | | // mh_bits = bit field of mh |
115 | 0 | let mh_bits = (EXP2F_TABLE[((k as u64) & TABLE_MASK) as usize] as i64).wrapping_add(exp_hi); |
116 | 0 | let mh = f64::from_bits(mh_bits as u64); |
117 | | |
118 | | // Degree-4 polynomial approximating (2^x - 1)/x generated by Sollya with: |
119 | | // > P = fpminimax((2^y - 1)/y, 4, [|D...|], [-1/64. 1/64]); |
120 | | // see ./notes/exp2f.sollya |
121 | | const C: [u64; 5] = [ |
122 | | 0x3fe62e42fefa39ef, |
123 | | 0x3fcebfbdff8131c4, |
124 | | 0x3fac6b08d7061695, |
125 | | 0x3f83b2b1bee74b2a, |
126 | | 0x3f55d88091198529, |
127 | | ]; |
128 | 0 | let dx_sq = dx * dx; |
129 | 0 | let c1 = f_fmla(dx, f64::from_bits(C[0]), 1.0); |
130 | 0 | let c2 = f_fmla(dx, f64::from_bits(C[2]), f64::from_bits(C[1])); |
131 | 0 | let c3 = f_fmla(dx, f64::from_bits(C[4]), f64::from_bits(C[3])); |
132 | 0 | let p = f_polyeval3(dx_sq, c1, c2, c3); |
133 | | // 2^x = 2^(hi + mid + lo) |
134 | | // = 2^(hi + mid) * 2^lo |
135 | | // ~ mh * (1 + lo * P(lo)) |
136 | | // = mh + (mh*lo) * P(lo) |
137 | 0 | f_fmla(p, mh, -1.) as f32 |
138 | 0 | } |
139 | | |
140 | | #[cfg(test)] |
141 | | mod tests { |
142 | | use super::*; |
143 | | |
144 | | #[test] |
145 | | fn test_exp2m1f() { |
146 | | assert_eq!(f_exp2m1f(0.432423), 0.34949815); |
147 | | assert_eq!(f_exp2m1f(-4.), -0.9375); |
148 | | assert_eq!(f_exp2m1f(5.43122), 42.14795); |
149 | | assert_eq!(f_exp2m1f(4.), 15.0); |
150 | | assert_eq!(f_exp2m1f(3.), 7.); |
151 | | assert_eq!(f_exp2m1f(0.1), 0.07177346); |
152 | | assert_eq!(f_exp2m1f(0.0543432432), 0.038386293); |
153 | | assert!(f_exp2m1f(f32::NAN).is_nan()); |
154 | | assert_eq!(f_exp2m1f(f32::INFINITY), f32::INFINITY); |
155 | | assert_eq!(f_exp2m1f(f32::NEG_INFINITY), -1.0); |
156 | | } |
157 | | } |