Skip to main content

pxfm/
pow.rs

1/*
2 * // Copyright (c) Radzivon Bartoshyk 4/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 */
29use crate::bits::get_exponent_f64;
30use crate::common::{f_fmla, is_integer, is_odd_integer};
31use crate::double_double::DoubleDouble;
32use crate::dyadic_float::{DyadicFloat128, DyadicSign};
33use crate::exponents::exp;
34use crate::logs::log_dyadic;
35use crate::pow_exec::{exp_dyadic, pow_exp_1, pow_log_1};
36use crate::triple_double::TripleDouble;
37use crate::{f_exp2, f_exp10, log};
38
39#[cold]
40fn pow_exp10_fallback(x: f64) -> f64 {
41    f_exp10(x)
42}
43
44#[cold]
45fn pow_exp2_fallback(x: f64) -> f64 {
46    f_exp2(x)
47}
48
49#[cold]
50#[inline(never)]
51fn f_powi(x: f64, y: f64) -> f64 {
52    let mut iter_count = unsafe { y.abs().to_int_unchecked::<usize>() };
53
54    let mut s = DoubleDouble::new(0., x);
55
56    // exponentiation by squaring: O(log(y)) complexity
57    let mut acc = if iter_count % 2 != 0 {
58        s
59    } else {
60        DoubleDouble::new(0., 1.)
61    };
62
63    while {
64        iter_count >>= 1;
65        iter_count
66    } != 0
67    {
68        s = DoubleDouble::mult(s, s);
69        if iter_count % 2 != 0 {
70            acc = DoubleDouble::mult(acc, s);
71        }
72    }
73
74    let dz = if y.is_sign_negative() {
75        acc.recip()
76    } else {
77        acc
78    };
79    let ub = dz.hi + f_fmla(f64::from_bits(0x3c40000000000000), -dz.hi, dz.lo); // 2^-59
80    let lb = dz.hi + f_fmla(f64::from_bits(0x3c40000000000000), dz.hi, dz.lo); // 2^-59
81    if ub == lb {
82        return dz.to_f64();
83    }
84    f_powi_hard(x, y)
85}
86
87#[cold]
88#[inline(never)]
89fn f_powi_hard(x: f64, y: f64) -> f64 {
90    let mut iter_count = unsafe { y.abs().to_int_unchecked::<usize>() };
91
92    let mut s = TripleDouble::new(0., 0., x);
93
94    // exponentiation by squaring: O(log(y)) complexity
95    let mut acc = if iter_count % 2 != 0 {
96        s
97    } else {
98        TripleDouble::new(0., 0., 1.)
99    };
100
101    while {
102        iter_count >>= 1;
103        iter_count
104    } != 0
105    {
106        s = TripleDouble::quick_mult(s, s);
107        if iter_count % 2 != 0 {
108            acc = TripleDouble::quick_mult(acc, s);
109        }
110    }
111
112    let dz = if y.is_sign_negative() {
113        acc.recip()
114    } else {
115        acc
116    };
117    dz.to_f64()
118}
119
120/// Power function
121///
122/// max found ULP 0.5
123pub fn f_pow(x: f64, y: f64) -> f64 {
124    let mut y = y;
125    let x_sign = x.is_sign_negative();
126    let y_sign = y.is_sign_negative();
127
128    let x_abs = x.to_bits() & 0x7fff_ffff_ffff_ffff;
129    let y_abs = y.to_bits() & 0x7fff_ffff_ffff_ffff;
130
131    const MANTISSA_MASK: u64 = (1u64 << 52) - 1;
132
133    let y_mant = y.to_bits() & MANTISSA_MASK;
134    let x_u = x.to_bits();
135    let x_a = x_abs;
136    let y_a = y_abs;
137
138    let mut x = x;
139
140    // If x or y is signaling NaN
141    if x.is_nan() || y.is_nan() {
142        return f64::NAN;
143    }
144
145    let mut s = 1.0;
146
147    // The double precision number that is closest to 1 is (1 - 2^-53), which has
148    //   log2(1 - 2^-53) ~ -1.715...p-53.
149    // So if |y| > |1075 / log2(1 - 2^-53)|, and x is finite:
150    //   |y * log2(x)| = 0 or > 1075.
151    // Hence, x^y will either overflow or underflow if x is not zero.
152    if y_mant == 0
153        || y_a > 0x43d7_4910_d52d_3052
154        || x_u == 1f64.to_bits()
155        || x_u >= f64::INFINITY.to_bits()
156        || x_u < f64::MIN.to_bits()
157    {
158        // Exceptional exponents.
159        if y == 0.0 {
160            return 1.0;
161        }
162
163        match y_a {
164            0x3fe0_0000_0000_0000 => {
165                if x == 0.0 || x_u == f64::NEG_INFINITY.to_bits() {
166                    // pow(-0, 1/2) = +0
167                    // pow(-inf, 1/2) = +inf
168                    return if y_sign { 1.0 / (x * x) } else { x * x };
169                }
170                return if y_sign {
171                    if x.is_infinite() {
172                        return if x.is_sign_positive() { 0. } else { f64::NAN };
173                    }
174                    #[cfg(any(
175                        all(
176                            any(target_arch = "x86", target_arch = "x86_64"),
177                            target_feature = "fma"
178                        ),
179                        target_arch = "aarch64"
180                    ))]
181                    {
182                        let r = x.sqrt() / x;
183                        let rx = r * x;
184                        let drx = f_fmla(r, x, -rx);
185                        let h = f_fmla(r, rx, -1.0) + r * drx;
186                        let dr = (r * 0.5) * h;
187                        r - dr
188                    }
189                    #[cfg(not(any(
190                        all(
191                            any(target_arch = "x86", target_arch = "x86_64"),
192                            target_feature = "fma"
193                        ),
194                        target_arch = "aarch64"
195                    )))]
196                    {
197                        let r = x.sqrt() / x;
198                        let d2x = DoubleDouble::from_exact_mult(r, x);
199                        let DoubleDouble { hi: h, lo: pr } = DoubleDouble::quick_mult_f64(d2x, r);
200                        let DoubleDouble { hi: p, lo: q } =
201                            DoubleDouble::from_full_exact_add(-1.0, h);
202                        let h = DoubleDouble::from_exact_add(p, pr + q);
203                        let dr = DoubleDouble::quick_mult_f64(h, r * 0.5);
204                        r - dr.hi - dr.lo
205                    }
206                } else {
207                    x.sqrt()
208                };
209            }
210            0x3ff0_0000_0000_0000 => {
211                return if y_sign { 1.0 / x } else { x };
212            }
213            0x4000_0000_0000_0000 => {
214                let x_e = get_exponent_f64(x);
215                if x_e > 511 {
216                    return if y_sign { 0. } else { f64::INFINITY };
217                }
218                // not enough precision to make 0.5 ULP for subnormals
219                let a_xe = x_e & 0x7fff_ffff_ffff_ffff;
220                if a_xe < 70 {
221                    let x_sqr = DoubleDouble::from_exact_mult(x, x);
222                    return if y_sign {
223                        let recip = x_sqr.recip();
224                        recip.to_f64()
225                    } else {
226                        x_sqr.to_f64()
227                    };
228                }
229            }
230            _ => {}
231        }
232
233        // |y| > |1075 / log2(1 - 2^-53)|.
234        if y_a > 0x43d7_4910_d52d_3052 {
235            if y_a >= 0x7ff0_0000_0000_0000 {
236                // y is inf or nan
237                if y_mant != 0 {
238                    // y is NaN
239                    // pow(1, NaN) = 1
240                    // pow(x, NaN) = NaN
241                    return if x_u == 1f64.to_bits() { 1.0 } else { y };
242                }
243
244                // Now y is +-Inf
245                if f64::from_bits(x_abs).is_nan() {
246                    // pow(NaN, +-Inf) = NaN
247                    return x;
248                }
249
250                if x_a == 0x3ff0_0000_0000_0000 {
251                    // pow(+-1, +-Inf) = 1.0
252                    return 1.0;
253                }
254
255                if x == 0.0 && y_sign {
256                    // pow(+-0, -Inf) = +inf and raise FE_DIVBYZERO
257                    return f64::INFINITY;
258                }
259                // pow (|x| < 1, -inf) = +inf
260                // pow (|x| < 1, +inf) = 0.0
261                // pow (|x| > 1, -inf) = 0.0
262                // pow (|x| > 1, +inf) = +inf
263                return if (x_a < 1f64.to_bits()) == y_sign {
264                    f64::INFINITY
265                } else {
266                    0.0
267                };
268            }
269            // x^y will overflow / underflow in double precision.  Set y to a
270            // large enough exponent but not too large, so that the computations
271            // won't overflow in double precision.
272            y = if y_sign {
273                f64::from_bits(0xc630000000000000)
274            } else {
275                f64::from_bits(0x4630000000000000)
276            };
277        }
278
279        // y is finite and non-zero.
280
281        if x_u == 1f64.to_bits() {
282            // pow(1, y) = 1
283            return 1.0;
284        }
285
286        if x == 0.0 {
287            let out_is_neg = x_sign && is_odd_integer(y);
288            if y_sign {
289                // pow(0, negative number) = inf
290                return if out_is_neg {
291                    f64::NEG_INFINITY
292                } else {
293                    f64::INFINITY
294                };
295            }
296            // pow(0, positive number) = 0
297            return if out_is_neg { -0.0 } else { 0.0 };
298        } else if x == 2.0 {
299            return pow_exp2_fallback(y);
300        } else if x == 10.0 {
301            return pow_exp10_fallback(y);
302        }
303
304        if x_a == f64::INFINITY.to_bits() {
305            let out_is_neg = x_sign && is_odd_integer(y);
306            if y_sign {
307                return if out_is_neg { -0.0 } else { 0.0 };
308            }
309            return if out_is_neg {
310                f64::NEG_INFINITY
311            } else {
312                f64::INFINITY
313            };
314        }
315
316        if x_a > f64::INFINITY.to_bits() {
317            // x is NaN.
318            // pow (aNaN, 0) is already taken care above.
319            return x;
320        }
321
322        // x is finite and negative, and y is a finite integer.
323
324        let is_y_integer = is_integer(y);
325        // y is integer and in [-102;102] and |x|<2^10
326
327        // this is correct only for positive exponent number without FMA,
328        // otherwise reciprocal may overflow.
329        #[cfg(any(
330            all(
331                any(target_arch = "x86", target_arch = "x86_64"),
332                target_feature = "fma"
333            ),
334            target_arch = "aarch64"
335        ))]
336        if is_y_integer
337            && y_a <= 0x4059800000000000u64
338            && x_a <= 0x4090000000000000u64
339            && x_a > 0x3cc0_0000_0000_0000
340        {
341            return f_powi(x, y);
342        }
343        #[cfg(not(any(
344            all(
345                any(target_arch = "x86", target_arch = "x86_64"),
346                target_feature = "fma"
347            ),
348            target_arch = "aarch64"
349        )))]
350        if is_y_integer
351            && y_a <= 0x4059800000000000u64
352            && x_a <= 0x4090000000000000u64
353            && x_a > 0x3cc0_0000_0000_0000
354            && y.is_sign_positive()
355        {
356            return f_powi(x, y);
357        }
358
359        if x_sign {
360            if is_y_integer {
361                x = -x;
362                if is_odd_integer(y) {
363                    // sign = -1.0;
364                    static CS: [f64; 2] = [1.0, -1.0];
365
366                    // set sign to 1 for y even, to -1 for y odd
367                    let y_parity = if (y.abs()) >= f64::from_bits(0x4340000000000000) {
368                        0usize
369                    } else {
370                        (y as i64 & 0x1) as usize
371                    };
372                    s = CS[y_parity];
373                }
374            } else {
375                // pow( negative, non-integer ) = NaN
376                return f64::NAN;
377            }
378        }
379
380        // y is finite and non-zero.
381
382        if x_u == 1f64.to_bits() {
383            // pow(1, y) = 1
384            return 1.0;
385        }
386
387        if x == 0.0 {
388            let out_is_neg = x_sign && is_odd_integer(y);
389            if y_sign {
390                // pow(0, negative number) = inf
391                return if out_is_neg {
392                    f64::NEG_INFINITY
393                } else {
394                    f64::INFINITY
395                };
396            }
397            // pow(0, positive number) = 0
398            return if out_is_neg { -0.0 } else { 0.0 };
399        }
400
401        if x_a == f64::INFINITY.to_bits() {
402            let out_is_neg = x_sign && is_odd_integer(y);
403            if y_sign {
404                return if out_is_neg { -0.0 } else { 0.0 };
405            }
406            return if out_is_neg {
407                f64::NEG_INFINITY
408            } else {
409                f64::INFINITY
410            };
411        }
412
413        if x_a > f64::INFINITY.to_bits() {
414            // x is NaN.
415            // pow (aNaN, 0) is already taken care above.
416            return x;
417        }
418    }
419
420    // approximate log(x)
421    let (mut l, cancel) = pow_log_1(x);
422
423    /* We should avoid a spurious underflow/overflow in y*log(x).
424    Underflow: for x<>1, the smallest absolute value of log(x) is obtained
425    for x=1-2^-53, with |log(x)| ~ 2^-53. Thus to avoid a spurious underflow
426    we require |y| >= 2^-969.
427    Overflow: the largest absolute value of log(x) is obtained for x=2^-1074,
428    with |log(x)| < 745. Thus to avoid a spurious overflow we require
429    |y| < 2^1014. */
430    let ey = ((y.to_bits() >> 52) & 0x7ff) as i32;
431    if ey < 0x36 || ey >= 0x7f5 {
432        l.lo = f64::NAN;
433        l.hi = f64::NAN;
434    }
435
436    let r = DoubleDouble::quick_mult_f64(l, y);
437    let res = pow_exp_1(r, s);
438    static ERR: [u64; 2] = [0x3bf2700000000000, 0x3c55700000000000];
439    let res_min = res.hi + f_fmla(f64::from_bits(ERR[cancel as usize]), -res.hi, res.lo);
440    let res_max = res.hi + f_fmla(f64::from_bits(ERR[cancel as usize]), res.hi, res.lo);
441    if res_min == res_max {
442        return res_max;
443    }
444    pow_rational128(x, y, s)
445}
446
447#[cold]
448fn pow_rational128(x: f64, y: f64, s: f64) -> f64 {
449    let f_y = DyadicFloat128::new_from_f64(y);
450
451    let r = log_dyadic(x) * f_y;
452    let mut result = exp_dyadic(r);
453
454    // 2^R.ex <= R < 2^(R.ex+1)
455
456    // /* case R < 2^-1075: underflow case */
457    // if result.exponent < -1075 {
458    //     return 0.5 * (s * f64::from_bits(0x0000000000000001));
459    // }
460
461    result.sign = if s == -1.0 {
462        DyadicSign::Neg
463    } else {
464        DyadicSign::Pos
465    };
466
467    result.fast_as_f64()
468}
469
470/// Pow for given value for const context.
471/// This is simplified version just to make a good approximation on const context.
472pub const fn pow(d: f64, n: f64) -> f64 {
473    let value = d.abs();
474
475    let r = n * log(value);
476    let c = exp(r);
477    if n == 0. {
478        return 1.;
479    }
480    if d < 0.0 {
481        let y = n as i32;
482        if y % 2 == 0 { c } else { -c }
483    } else {
484        c
485    }
486}
487
488#[cfg(test)]
489mod tests {
490    use super::*;
491
492    #[test]
493    fn powf_test() {
494        assert!(
495            (pow(2f64, 3f64) - 8f64).abs() < 1e-9,
496            "Invalid result {}",
497            pow(2f64, 3f64)
498        );
499        assert!(
500            (pow(0.5f64, 2f64) - 0.25f64).abs() < 1e-9,
501            "Invalid result {}",
502            pow(0.5f64, 2f64)
503        );
504    }
505
506    #[test]
507    fn f_pow_test() {
508        assert_eq!(f_pow(
509             0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000135264499699371,
510            -0.5,
511        ), 27189929701044785000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.);
512        assert_eq!(f_pow(
513            0.000000000000000000000000000000000000000000000000000021798599361155193,
514            -2.,
515        ),2104470396771397700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.);
516        assert_eq!(
517            f_pow(-25192281723900620000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.,
518            -2.),
519            0.
520        );
521        assert_eq!(
522            f_pow(0.000000000000000000000000000000000000000000000000000021799650661798696,
523                  -2.),
524            2104267423084451500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.
525        );
526        assert_eq!(
527            f_pow(0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014916691520383755,
528            -2.),
529            44942267764413600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.
530        );
531        assert_eq!(
532            f_pow(
533                0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000135264499699371,
534                -0.5,
535            ),
536            27189929701044785000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.
537        );
538        assert_eq!(f_pow(1., f64::INFINITY), 1.);
539        assert_eq!(f_pow(2., f64::INFINITY), f64::INFINITY);
540        assert_eq!(f_pow(f64::INFINITY, -0.5), 0.);
541        assert!(
542            (f_pow(2f64, 3f64) - 8f64).abs() < 1e-9,
543            "Invalid result {}",
544            f_pow(2f64, 3f64)
545        );
546        assert!(
547            (f_pow(0.5f64, 2f64) - 0.25f64).abs() < 1e-9,
548            "Invalid result {}",
549            f_pow(0.5f64, 2f64)
550        );
551        assert_eq!(f_pow(2.1f64, 2.7f64), 7.412967494768546);
552        assert_eq!(f_pow(27., 1. / 3.), 3.);
553    }
554
555    #[test]
556    fn powi_test() {
557        assert_eq!(f_pow(f64::from_bits(0x3cc0_0000_0000_0000), 102.), 0.0);
558        assert_eq!(f_pow(3., 3.), 27.);
559        assert_eq!(f_pow(3., -3.), 1. / 27.);
560        assert_eq!(f_pow(3., 102.), 4.638397686588102e48);
561        assert_eq!(f_pow(0.000000000000011074474670636028, -22.), 10589880229528372000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.);
562    }
563}