/rust/registry/src/index.crates.io-1949cf8c6b5b557f/pxfm-0.1.25/src/exponents/exp10.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; |
30 | | use crate::double_double::DoubleDouble; |
31 | | use crate::exponents::auxiliary::fast_ldexp; |
32 | | use crate::exponents::exp::{EXP_REDUCE_T0, EXP_REDUCE_T1, to_denormal}; |
33 | | use crate::rounding::CpuRoundTiesEven; |
34 | | use std::hint::black_box; |
35 | | |
36 | | #[inline] |
37 | 0 | fn exp10_poly_dd(z: DoubleDouble) -> DoubleDouble { |
38 | | const C: [(u64, u64); 6] = [ |
39 | | (0xbcaf48ad494ea102, 0x40026bb1bbb55516), |
40 | | (0xbcae2bfab318d399, 0x40053524c73cea69), |
41 | | (0x3ca81f50779e162b, 0x4000470591de2ca4), |
42 | | (0x3c931a5cc5d3d313, 0x3ff2bd7609fd98c4), |
43 | | (0x3c8910de8c68a0c2, 0x3fe1429ffd336aa3), |
44 | | (0xbc605e703d496537, 0x3fca7ed7086882b4), |
45 | | ]; |
46 | | |
47 | 0 | let mut r = DoubleDouble::quick_mul_add( |
48 | 0 | DoubleDouble::from_bit_pair(C[5]), |
49 | 0 | z, |
50 | 0 | DoubleDouble::from_bit_pair(C[4]), |
51 | | ); |
52 | 0 | r = DoubleDouble::quick_mul_add(r, z, DoubleDouble::from_bit_pair(C[3])); |
53 | 0 | r = DoubleDouble::quick_mul_add(r, z, DoubleDouble::from_bit_pair(C[2])); |
54 | 0 | r = DoubleDouble::quick_mul_add(r, z, DoubleDouble::from_bit_pair(C[1])); |
55 | 0 | DoubleDouble::quick_mul_add(r, z, DoubleDouble::from_bit_pair(C[0])) |
56 | 0 | } |
57 | | |
58 | | #[cold] |
59 | 0 | fn as_exp10_accurate(x: f64) -> f64 { |
60 | 0 | let mut ix = x.to_bits(); |
61 | 0 | let t = (f64::from_bits(0x40ca934f0979a371) * x).cpu_round_ties_even(); |
62 | 0 | let jt: i64 = unsafe { |
63 | 0 | t.to_int_unchecked::<i64>() // t is already integer, this is just a conversion |
64 | | }; |
65 | 0 | let i1 = jt & 0x3f; |
66 | 0 | let i0 = (jt >> 6) & 0x3f; |
67 | 0 | let ie = jt >> 12; |
68 | 0 | let t0 = DoubleDouble::from_bit_pair(EXP_REDUCE_T0[i0 as usize]); |
69 | 0 | let t1 = DoubleDouble::from_bit_pair(EXP_REDUCE_T1[i1 as usize]); |
70 | 0 | let dt = DoubleDouble::quick_mult(t0, t1); |
71 | | |
72 | | const L0: f64 = f64::from_bits(0x3f13441350800000); |
73 | | const L1: f64 = f64::from_bits(0xbd1f79fef311f12b); |
74 | | const L2: f64 = f64::from_bits(0xb9aac0b7c917826b); |
75 | | |
76 | 0 | let dx = x - L0 * t; |
77 | 0 | let dx_dd = DoubleDouble::quick_mult_f64(DoubleDouble::new(L2, L1), t); |
78 | 0 | let dz = DoubleDouble::full_add_f64(dx_dd, dx); |
79 | 0 | let mut f = exp10_poly_dd(dz); |
80 | 0 | f = DoubleDouble::quick_mult(dz, f); |
81 | | |
82 | | let mut zfh: f64; |
83 | | |
84 | 0 | if ix < 0xc0733a7146f72a42u64 { |
85 | 0 | if (jt & 0xfff) == 0 { |
86 | 0 | f = DoubleDouble::from_exact_add(f.hi, f.lo); |
87 | 0 | let zt = DoubleDouble::from_exact_add(dt.hi, f.hi); |
88 | 0 | f.hi = zt.lo; |
89 | 0 | f = DoubleDouble::from_exact_add(f.hi, f.lo); |
90 | 0 | ix = f.hi.to_bits(); |
91 | 0 | if (ix.wrapping_shl(12)) == 0 { |
92 | 0 | let l = f.lo.to_bits(); |
93 | 0 | let sfh: i64 = ((ix as i64) >> 63) ^ ((l as i64) >> 63); |
94 | 0 | ix = ix.wrapping_add(((1i64 << 51) ^ sfh) as u64); |
95 | 0 | } |
96 | 0 | zfh = zt.hi + f64::from_bits(ix); |
97 | 0 | } else { |
98 | 0 | f = DoubleDouble::quick_mult(f, dt); |
99 | 0 | f = DoubleDouble::add(dt, f); |
100 | 0 | f = DoubleDouble::from_exact_add(f.hi, f.lo); |
101 | 0 | zfh = f.hi; |
102 | 0 | } |
103 | 0 | zfh = fast_ldexp(zfh, ie as i32); |
104 | 0 | } else { |
105 | 0 | ix = (1u64.wrapping_sub(ie as u64)) << 52; |
106 | 0 | f = DoubleDouble::quick_mult(f, dt); |
107 | 0 | f = DoubleDouble::add(dt, f); |
108 | 0 |
|
109 | 0 | let zt = DoubleDouble::from_exact_add(f64::from_bits(ix), f.hi); |
110 | 0 | f.hi = zt.hi; |
111 | 0 | f.lo += zt.lo; |
112 | 0 |
|
113 | 0 | zfh = to_denormal(f.to_f64()); |
114 | 0 | } |
115 | 0 | zfh |
116 | 0 | } |
117 | | |
118 | | /// Computes exp10 |
119 | | /// |
120 | | /// Max found ULP 0.5 |
121 | 0 | pub fn f_exp10(x: f64) -> f64 { |
122 | 0 | let mut ix = x.to_bits(); |
123 | 0 | let aix = ix & 0x7fff_ffff_ffff_ffff; |
124 | 0 | if aix > 0x40734413509f79feu64 { |
125 | | // |x| > 0x40734413509f79fe |
126 | 0 | if aix > 0x7ff0000000000000u64 { |
127 | 0 | return x + x; |
128 | 0 | } // nan |
129 | 0 | if aix == 0x7ff0000000000000u64 { |
130 | 0 | return if (ix >> 63) != 0 { 0.0 } else { x }; |
131 | 0 | } |
132 | 0 | if (ix >> 63) == 0 { |
133 | 0 | return f64::from_bits(0x7fe0000000000000) * 2.0; // x > 308.255 |
134 | 0 | } |
135 | 0 | if aix > 0x407439b746e36b52u64 { |
136 | | // x < -323.607 |
137 | 0 | return black_box(f64::from_bits(0x0018000000000000)) |
138 | 0 | * black_box(f64::from_bits(0x3c80000000000000)); |
139 | 0 | } |
140 | 0 | } |
141 | | |
142 | | // check x integer to avoid a spurious inexact exception |
143 | 0 | if ix.wrapping_shl(16) == 0 && (aix >> 48) <= 0x4036 { |
144 | 0 | let kx = x.cpu_round_ties_even(); |
145 | 0 | if kx == x { |
146 | 0 | let k = kx as i64; |
147 | 0 | if k >= 0 { |
148 | 0 | let mut r = 1.0; |
149 | 0 | for _ in 0..k { |
150 | 0 | r *= 10.0; |
151 | 0 | } |
152 | 0 | return r; |
153 | 0 | } |
154 | 0 | } |
155 | 0 | } |
156 | | /* avoid spurious underflow: for |x| <= 2.41082e-17 |
157 | | exp10(x) rounds to 1 to nearest */ |
158 | 0 | if aix <= 0x3c7bcb7b1526e50eu64 { |
159 | 0 | return 1.0 + x; // |x| <= 2.41082e-17 |
160 | 0 | } |
161 | 0 | let t = (f64::from_bits(0x40ca934f0979a371) * x).cpu_round_ties_even(); |
162 | 0 | let jt: i64 = unsafe { t.to_int_unchecked::<i64>() }; // t is already integer this is just a conversion |
163 | 0 | let i1 = jt & 0x3f; |
164 | 0 | let i0 = (jt >> 6) & 0x3f; |
165 | 0 | let ie = jt >> 12; |
166 | 0 | let t00 = EXP_REDUCE_T0[i0 as usize]; |
167 | 0 | let t01 = EXP_REDUCE_T1[i1 as usize]; |
168 | 0 | let t0 = DoubleDouble::from_bit_pair(t00); |
169 | 0 | let t1 = DoubleDouble::from_bit_pair(t01); |
170 | 0 | let mut tz = DoubleDouble::quick_mult(t0, t1); |
171 | | const L0: f64 = f64::from_bits(0x3f13441350800000); |
172 | | const L1: f64 = f64::from_bits(0x3d1f79fef311f12b); |
173 | 0 | let dx = f_fmla(-L1, t, f_fmla(-L0, t, x)); |
174 | 0 | let dx2 = dx * dx; |
175 | | |
176 | | const CH: [u64; 4] = [ |
177 | | 0x40026bb1bbb55516, |
178 | | 0x40053524c73cea69, |
179 | | 0x4000470591fd74e1, |
180 | | 0x3ff2bd760a1f32a5, |
181 | | ]; |
182 | | |
183 | 0 | let p0 = f_fmla(dx, f64::from_bits(CH[1]), f64::from_bits(CH[0])); |
184 | 0 | let p1 = f_fmla(dx, f64::from_bits(CH[3]), f64::from_bits(CH[2])); |
185 | | |
186 | 0 | let p = f_fmla(dx2, p1, p0); |
187 | | |
188 | 0 | let mut fh = tz.hi; |
189 | 0 | let fx = tz.hi * dx; |
190 | 0 | let mut fl = f_fmla(fx, p, tz.lo); |
191 | | const EPS: f64 = 1.63e-19; |
192 | 0 | if ix < 0xc0733a7146f72a42u64 { |
193 | | // x > -307.653 |
194 | | // x > -0x1.33a7146f72a42p+8 |
195 | 0 | let ub = fh + (fl + EPS); |
196 | 0 | let lb = fh + (fl - EPS); |
197 | | |
198 | 0 | if lb != ub { |
199 | 0 | return as_exp10_accurate(x); |
200 | 0 | } |
201 | 0 | fh = fast_ldexp(fh + fl, ie as i32); |
202 | | } else { |
203 | | // x <= -307.653: exp10(x) < 2^-1022 |
204 | 0 | ix = 1u64.wrapping_sub(ie as u64).wrapping_shl(52); |
205 | 0 | tz = DoubleDouble::from_exact_add(f64::from_bits(ix), fh); |
206 | 0 | fl += tz.lo; |
207 | | |
208 | 0 | let ub = fh + (fl + EPS); |
209 | 0 | let lb = fh + (fl - EPS); |
210 | | |
211 | 0 | if lb != ub { |
212 | 0 | return as_exp10_accurate(x); |
213 | 0 | } |
214 | | |
215 | 0 | fh = to_denormal(fh + fl); |
216 | | } |
217 | 0 | fh |
218 | 0 | } |
219 | | |
220 | | #[cfg(test)] |
221 | | mod tests { |
222 | | use super::*; |
223 | | |
224 | | #[test] |
225 | | fn test_exp10f() { |
226 | | assert_eq!(f_exp10(-3.370739843267434), 0.00042585343701025656); |
227 | | assert_eq!(f_exp10(1.), 10.0); |
228 | | assert_eq!(f_exp10(2.), 100.0); |
229 | | assert_eq!(f_exp10(3.), 1000.0); |
230 | | assert_eq!(f_exp10(4.), 10000.0); |
231 | | assert_eq!(f_exp10(5.), 100000.0); |
232 | | assert_eq!(f_exp10(6.), 1000000.0); |
233 | | assert_eq!(f_exp10(7.), 10000000.0); |
234 | | assert_eq!(f_exp10(f64::INFINITY), f64::INFINITY); |
235 | | assert_eq!(f_exp10(f64::NEG_INFINITY), 0.); |
236 | | assert!(f_exp10(f64::NAN).is_nan()); |
237 | | } |
238 | | } |