|
1 |
| -/* origin: FreeBSD /usr/src/lib/msun/src/s_cbrt.c */ |
2 |
| -/* |
3 |
| - * ==================================================== |
4 |
| - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. |
5 |
| - * |
6 |
| - * Developed at SunPro, a Sun Microsystems, Inc. business. |
7 |
| - * Permission to use, copy, modify, and distribute this |
8 |
| - * software is freely granted, provided that this notice |
9 |
| - * is preserved. |
10 |
| - * ==================================================== |
11 |
| - * |
12 |
| - * Optimized by Bruce D. Evans. |
| 1 | +/* SPDX-License-Identifier: MIT */ |
| 2 | +/* origin: core-math/src/binary64/cbrt/cbrt.c |
| 3 | + * Copyright (c) 2021-2022 Alexei Sibidanov. |
| 4 | + * Ported to Rust in 2025 by Trevor Gross. |
13 | 5 | */
|
14 |
| -/* cbrt(x) |
15 |
| - * Return cube root of x |
16 |
| - */ |
17 |
| - |
18 |
| -use core::f64; |
19 | 6 |
|
20 |
| -const B1: u32 = 715094163; /* B1 = (1023-1023/3-0.03306235651)*2**20 */ |
21 |
| -const B2: u32 = 696219795; /* B2 = (1023-1023/3-54/3-0.03306235651)*2**20 */ |
| 7 | +use super::Float; |
| 8 | +use super::fenv::Rounding; |
| 9 | +use super::support::cold_path; |
22 | 10 |
|
23 |
| -/* |1/cbrt(x) - p(x)| < 2**-23.5 (~[-7.93e-8, 7.929e-8]). */ |
24 |
| -const P0: f64 = 1.87595182427177009643; /* 0x3ffe03e6, 0x0f61e692 */ |
25 |
| -const P1: f64 = -1.88497979543377169875; /* 0xbffe28e0, 0x92f02420 */ |
26 |
| -const P2: f64 = 1.621429720105354466140; /* 0x3ff9f160, 0x4a49d6c2 */ |
27 |
| -const P3: f64 = -0.758397934778766047437; /* 0xbfe844cb, 0xbee751d9 */ |
28 |
| -const P4: f64 = 0.145996192886612446982; /* 0x3fc2b000, 0xd4e4edd7 */ |
29 |
| - |
30 |
| -// Cube root (f64) |
31 |
| -/// |
32 |
| -/// Computes the cube root of the argument. |
| 11 | +/// Compute the cube root of the argument. |
33 | 12 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
|
34 | 13 | pub fn cbrt(x: f64) -> f64 {
|
35 |
| - let x1p54 = f64::from_bits(0x4350000000000000); // 0x1p54 === 2 ^ 54 |
36 |
| - |
37 |
| - let mut ui: u64 = x.to_bits(); |
38 |
| - let mut r: f64; |
39 |
| - let s: f64; |
40 |
| - let mut t: f64; |
41 |
| - let w: f64; |
42 |
| - let mut hx: u32 = (ui >> 32) as u32 & 0x7fffffff; |
43 |
| - |
44 |
| - if hx >= 0x7ff00000 { |
45 |
| - /* cbrt(NaN,INF) is itself */ |
46 |
| - return x + x; |
| 14 | + const ESCALE: [f64; 3] = [ |
| 15 | + 1.0, |
| 16 | + hf64!("0x1.428a2f98d728bp+0"), /* 2^(1/3) */ |
| 17 | + hf64!("0x1.965fea53d6e3dp+0"), /* 2^(2/3) */ |
| 18 | + ]; |
| 19 | + |
| 20 | + /* the polynomial c0+c1*x+c2*x^2+c3*x^3 approximates x^(1/3) on [1,2] |
| 21 | + with maximal error < 9.2e-5 (attained at x=2) */ |
| 22 | + const C: [f64; 4] = [ |
| 23 | + hf64!("0x1.1b0babccfef9cp-1"), |
| 24 | + hf64!("0x1.2c9a3e94d1da5p-1"), |
| 25 | + hf64!("-0x1.4dc30b1a1ddbap-3"), |
| 26 | + hf64!("0x1.7a8d3e4ec9b07p-6"), |
| 27 | + ]; |
| 28 | + |
| 29 | + let u0: f64 = hf64!("0x1.5555555555555p-2"); |
| 30 | + let u1: f64 = hf64!("0x1.c71c71c71c71cp-3"); |
| 31 | + |
| 32 | + let rsc = [1.0, -1.0, 0.5, -0.5, 0.25, -0.25]; |
| 33 | + |
| 34 | + let off = [hf64!("0x1p-53"), 0.0, 0.0, 0.0]; |
| 35 | + |
| 36 | + let rm = Rounding::get(); |
| 37 | + |
| 38 | + /* rm=0 for rounding to nearest, and other values for directed roundings */ |
| 39 | + let hx: u64 = x.to_bits(); |
| 40 | + let mut mant: u64 = hx & f64::SIG_MASK; |
| 41 | + let sign: u64 = hx >> 63; |
| 42 | + |
| 43 | + let mut e: u32 = (hx >> f64::SIG_BITS) as u32 & f64::EXP_SAT; |
| 44 | + |
| 45 | + if ((e + 1) & f64::EXP_SAT) < 2 { |
| 46 | + cold_path(); |
| 47 | + |
| 48 | + let ix: u64 = hx & !f64::SIGN_MASK; |
| 49 | + |
| 50 | + /* 0, inf, nan: we return x + x instead of simply x, |
| 51 | + to that for x a signaling NaN, it correctly triggers |
| 52 | + the invalid exception. */ |
| 53 | + if e == f64::EXP_SAT || ix == 0 { |
| 54 | + return x + x; |
| 55 | + } |
| 56 | + |
| 57 | + let nz = ix.leading_zeros() - 11; /* subnormal */ |
| 58 | + mant <<= nz; |
| 59 | + mant &= f64::SIG_MASK; |
| 60 | + e = e.wrapping_sub(nz - 1); |
| 61 | + } |
| 62 | + |
| 63 | + e = e.wrapping_add(3072); |
| 64 | + let cvt1: u64 = mant | (0x3ffu64 << 52); |
| 65 | + let mut cvt5: u64 = cvt1; |
| 66 | + |
| 67 | + let et: u32 = e / 3; |
| 68 | + let it: u32 = e % 3; |
| 69 | + |
| 70 | + /* 2^(3k+it) <= x < 2^(3k+it+1), with 0 <= it <= 3 */ |
| 71 | + cvt5 += u64::from(it) << f64::SIG_BITS; |
| 72 | + cvt5 |= sign << 63; |
| 73 | + let zz: f64 = f64::from_bits(cvt5); |
| 74 | + |
| 75 | + /* cbrt(x) = cbrt(zz)*2^(et-1365) where 1 <= zz < 8 */ |
| 76 | + let mut isc: u64 = ESCALE[it as usize].to_bits(); // todo: index |
| 77 | + isc |= sign << 63; |
| 78 | + let cvt2: u64 = isc; |
| 79 | + let z: f64 = f64::from_bits(cvt1); |
| 80 | + |
| 81 | + /* cbrt(zz) = cbrt(z)*isc, where isc encodes 1, 2^(1/3) or 2^(2/3), |
| 82 | + and 1 <= z < 2 */ |
| 83 | + let r: f64 = 1.0 / z; |
| 84 | + let rr: f64 = r * rsc[((it as usize) << 1) | sign as usize]; |
| 85 | + let z2: f64 = z * z; |
| 86 | + let c0: f64 = C[0] + z * C[1]; |
| 87 | + let c2: f64 = C[2] + z * C[3]; |
| 88 | + let mut y: f64 = c0 + z2 * c2; |
| 89 | + let mut y2: f64 = y * y; |
| 90 | + |
| 91 | + /* y is an approximation of z^(1/3) */ |
| 92 | + let mut h: f64 = y2 * (y * r) - 1.0; |
| 93 | + |
| 94 | + /* h determines the error between y and z^(1/3) */ |
| 95 | + y -= (h * y) * (u0 - u1 * h); |
| 96 | + |
| 97 | + /* The correction y -= (h*y)*(u0 - u1*h) corresponds to a cubic variant |
| 98 | + of Newton's method, with the function f(y) = 1-z/y^3. */ |
| 99 | + y *= f64::from_bits(cvt2); |
| 100 | + |
| 101 | + /* Now y is an approximation of zz^(1/3), |
| 102 | + * and rr an approximation of 1/zz. We now perform another iteration of |
| 103 | + * Newton-Raphson, this time with a linear approximation only. */ |
| 104 | + y2 = y * y; |
| 105 | + let mut y2l: f64 = fmaf64(y, y, -y2); |
| 106 | + |
| 107 | + /* y2 + y2l = y^2 exactly */ |
| 108 | + let mut y3: f64 = y2 * y; |
| 109 | + let mut y3l: f64 = fmaf64(y, y2, -y3) + y * y2l; |
| 110 | + |
| 111 | + /* y3 + y3l approximates y^3 with about 106 bits of accuracy */ |
| 112 | + h = ((y3 - zz) + y3l) * rr; |
| 113 | + let mut dy: f64 = h * (y * u0); |
| 114 | + |
| 115 | + /* the approximation of zz^(1/3) is y - dy */ |
| 116 | + let mut y1: f64 = y - dy; |
| 117 | + dy = (y - y1) - dy; |
| 118 | + |
| 119 | + /* the approximation of zz^(1/3) is now y1 + dy, where |dy| < 1/2 ulp(y) |
| 120 | + * (for rounding to nearest) */ |
| 121 | + let mut ady: f64 = dy.abs(); |
| 122 | + |
| 123 | + /* For directed roundings, ady0 is tiny when dy is tiny, or ady0 is near |
| 124 | + * from ulp(1); |
| 125 | + * for rounding to nearest, ady0 is tiny when dy is near from 1/2 ulp(1), |
| 126 | + * or from 3/2 ulp(1). */ |
| 127 | + let mut ady0: f64 = (ady - off[rm as usize]).abs(); |
| 128 | + let mut ady1: f64 = (ady - (hf64!("0x1p-52") + off[rm as usize])).abs(); |
| 129 | + |
| 130 | + if ady0 < hf64!("0x1p-75") || ady1 < hf64!("0x1p-75") { |
| 131 | + cold_path(); |
| 132 | + |
| 133 | + y2 = y1 * y1; |
| 134 | + y2l = fmaf64(y1, y1, -y2); |
| 135 | + y3 = y2 * y1; |
| 136 | + y3l = fmaf64(y1, y2, -y3) + y1 * y2l; |
| 137 | + h = ((y3 - zz) + y3l) * rr; |
| 138 | + dy = h * (y1 * u0); |
| 139 | + y = y1 - dy; |
| 140 | + dy = (y1 - y) - dy; |
| 141 | + y1 = y; |
| 142 | + ady = dy.abs(); |
| 143 | + ady0 = (ady - off[rm as usize]).abs(); |
| 144 | + ady1 = (ady - (hf64!("0x1p-52") + off[rm as usize])).abs(); |
| 145 | + |
| 146 | + if ady0 < hf64!("0x1p-98") || ady1 < hf64!("0x1p-98") { |
| 147 | + cold_path(); |
| 148 | + let azz: f64 = zz.abs(); |
| 149 | + |
| 150 | + // ~ 0x1.79d15d0e8d59b80000000000000ffc3dp+0 |
| 151 | + if azz == hf64!("0x1.9b78223aa307cp+1") { |
| 152 | + y1 = hf64!("0x1.79d15d0e8d59cp+0").copysign(zz); |
| 153 | + } |
| 154 | + |
| 155 | + // ~ 0x1.de87aa837820e80000000000001c0f08p+0 |
| 156 | + if azz == hf64!("0x1.a202bfc89ddffp+2") { |
| 157 | + y1 = hf64!("0x1.de87aa837820fp+0").copysign(zz); |
| 158 | + } |
| 159 | + |
| 160 | + if rm != Rounding::Nearest { |
| 161 | + let wlist = [ |
| 162 | + (hf64!("0x1.3a9ccd7f022dbp+0"), hf64!("0x1.1236160ba9b93p+0")), // ~ 0x1.1236160ba9b930000000000001e7e8fap+0 |
| 163 | + (hf64!("0x1.7845d2faac6fep+0"), hf64!("0x1.23115e657e49cp+0")), // ~ 0x1.23115e657e49c0000000000001d7a799p+0 |
| 164 | + (hf64!("0x1.d1ef81cbbbe71p+0"), hf64!("0x1.388fb44cdcf5ap+0")), // ~ 0x1.388fb44cdcf5a0000000000002202c55p+0 |
| 165 | + (hf64!("0x1.0a2014f62987cp+1"), hf64!("0x1.46bcbf47dc1e8p+0")), // ~ 0x1.46bcbf47dc1e8000000000000303aa2dp+0 |
| 166 | + (hf64!("0x1.fe18a044a5501p+1"), hf64!("0x1.95decfec9c904p+0")), // ~ 0x1.95decfec9c9040000000000000159e8ep+0 |
| 167 | + (hf64!("0x1.a6bb8c803147bp+2"), hf64!("0x1.e05335a6401dep+0")), // ~ 0x1.e05335a6401de00000000000027ca017p+0 |
| 168 | + (hf64!("0x1.ac8538a031cbdp+2"), hf64!("0x1.e281d87098de8p+0")), // ~ 0x1.e281d87098de80000000000000ee9314p+0 |
| 169 | + ]; |
| 170 | + |
| 171 | + for (a, b) in wlist { |
| 172 | + if azz == a { |
| 173 | + let tmp = if rm as u64 + sign == 2 { hf64!("0x1p-52") } else { 0.0 }; |
| 174 | + y1 = (b + tmp).copysign(zz); |
| 175 | + } |
| 176 | + } |
| 177 | + } |
| 178 | + } |
| 179 | + } |
| 180 | + |
| 181 | + let mut cvt3: u64 = y1.to_bits(); |
| 182 | + cvt3 = cvt3.wrapping_add(((et.wrapping_sub(342).wrapping_sub(1023)) as u64) << 52); |
| 183 | + let m0: u64 = cvt3 << 30; |
| 184 | + let m1 = m0 >> 63; |
| 185 | + |
| 186 | + if (m0 ^ m1) <= (1u64 << 30) { |
| 187 | + cold_path(); |
| 188 | + |
| 189 | + let mut cvt4: u64 = y1.to_bits(); |
| 190 | + cvt4 = (cvt4 + (164 << 15)) & 0xffffffffffff0000u64; |
| 191 | + |
| 192 | + if ((f64::from_bits(cvt4) - y1) - dy).abs() < hf64!("0x1p-60") || (zz).abs() == 1.0 { |
| 193 | + cvt3 = (cvt3 + (1u64 << 15)) & 0xffffffffffff0000u64; |
| 194 | + } |
47 | 195 | }
|
48 | 196 |
|
49 |
| - /* |
50 |
| - * Rough cbrt to 5 bits: |
51 |
| - * cbrt(2**e*(1+m) ~= 2**(e/3)*(1+(e%3+m)/3) |
52 |
| - * where e is integral and >= 0, m is real and in [0, 1), and "/" and |
53 |
| - * "%" are integer division and modulus with rounding towards minus |
54 |
| - * infinity. The RHS is always >= the LHS and has a maximum relative |
55 |
| - * error of about 1 in 16. Adding a bias of -0.03306235651 to the |
56 |
| - * (e%3+m)/3 term reduces the error to about 1 in 32. With the IEEE |
57 |
| - * floating point representation, for finite positive normal values, |
58 |
| - * ordinary integer divison of the value in bits magically gives |
59 |
| - * almost exactly the RHS of the above provided we first subtract the |
60 |
| - * exponent bias (1023 for doubles) and later add it back. We do the |
61 |
| - * subtraction virtually to keep e >= 0 so that ordinary integer |
62 |
| - * division rounds towards minus infinity; this is also efficient. |
63 |
| - */ |
64 |
| - if hx < 0x00100000 { |
65 |
| - /* zero or subnormal? */ |
66 |
| - ui = (x * x1p54).to_bits(); |
67 |
| - hx = (ui >> 32) as u32 & 0x7fffffff; |
68 |
| - if hx == 0 { |
69 |
| - return x; /* cbrt(0) is itself */ |
| 197 | + f64::from_bits(cvt3) |
| 198 | +} |
| 199 | + |
| 200 | +fn fmaf64(x: f64, y: f64, z: f64) -> f64 { |
| 201 | + #[cfg(intrinsics_enabled)] |
| 202 | + { |
| 203 | + return unsafe { core::intrinsics::fmaf64(x, y, z) }; |
| 204 | + } |
| 205 | + |
| 206 | + #[cfg(not(intrinsics_enabled))] |
| 207 | + { |
| 208 | + return super::fma(x, y, z); |
| 209 | + } |
| 210 | +} |
| 211 | + |
| 212 | +#[cfg(test)] |
| 213 | +mod tests { |
| 214 | + use super::*; |
| 215 | + |
| 216 | + #[test] |
| 217 | + fn spot_checks() { |
| 218 | + if !cfg!(x86_no_sse) { |
| 219 | + // Exposes a rounding mode problem. Ignored on i586 because of inaccurate FMA. |
| 220 | + assert_biteq!( |
| 221 | + cbrt(f64::from_bits(0xf7f792b28f600000)), |
| 222 | + f64::from_bits(0xd29ce68655d962f3) |
| 223 | + ); |
70 | 224 | }
|
71 |
| - hx = hx / 3 + B2; |
72 |
| - } else { |
73 |
| - hx = hx / 3 + B1; |
74 | 225 | }
|
75 |
| - ui &= 1 << 63; |
76 |
| - ui |= (hx as u64) << 32; |
77 |
| - t = f64::from_bits(ui); |
78 |
| - |
79 |
| - /* |
80 |
| - * New cbrt to 23 bits: |
81 |
| - * cbrt(x) = t*cbrt(x/t**3) ~= t*P(t**3/x) |
82 |
| - * where P(r) is a polynomial of degree 4 that approximates 1/cbrt(r) |
83 |
| - * to within 2**-23.5 when |r - 1| < 1/10. The rough approximation |
84 |
| - * has produced t such than |t/cbrt(x) - 1| ~< 1/32, and cubing this |
85 |
| - * gives us bounds for r = t**3/x. |
86 |
| - * |
87 |
| - * Try to optimize for parallel evaluation as in __tanf.c. |
88 |
| - */ |
89 |
| - r = (t * t) * (t / x); |
90 |
| - t = t * ((P0 + r * (P1 + r * P2)) + ((r * r) * r) * (P3 + r * P4)); |
91 |
| - |
92 |
| - /* |
93 |
| - * Round t away from zero to 23 bits (sloppily except for ensuring that |
94 |
| - * the result is larger in magnitude than cbrt(x) but not much more than |
95 |
| - * 2 23-bit ulps larger). With rounding towards zero, the error bound |
96 |
| - * would be ~5/6 instead of ~4/6. With a maximum error of 2 23-bit ulps |
97 |
| - * in the rounded t, the infinite-precision error in the Newton |
98 |
| - * approximation barely affects third digit in the final error |
99 |
| - * 0.667; the error in the rounded t can be up to about 3 23-bit ulps |
100 |
| - * before the final error is larger than 0.667 ulps. |
101 |
| - */ |
102 |
| - ui = t.to_bits(); |
103 |
| - ui = (ui + 0x80000000) & 0xffffffffc0000000; |
104 |
| - t = f64::from_bits(ui); |
105 |
| - |
106 |
| - /* one step Newton iteration to 53 bits with error < 0.667 ulps */ |
107 |
| - s = t * t; /* t*t is exact */ |
108 |
| - r = x / s; /* error <= 0.5 ulps; |r| < |t| */ |
109 |
| - w = t + t; /* t+t is exact */ |
110 |
| - r = (r - t) / (w + r); /* r-t is exact; w+r ~= 3*t */ |
111 |
| - t = t + t * r; /* error <= 0.5 + 0.5/3 + epsilon */ |
112 |
| - t |
113 | 226 | }
|
0 commit comments