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 = y.abs() as 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                        all(target_arch = "aarch64", target_feature = "neon")
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                        all(target_arch = "aarch64", target_feature = "neon")
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                if x_e.abs() < 70 {
220                    let x_sqr = DoubleDouble::from_exact_mult(x, x);
221                    return if y_sign {
222                        let recip = x_sqr.recip();
223                        recip.to_f64()
224                    } else {
225                        x_sqr.to_f64()
226                    };
227                }
228            }
229            _ => {}
230        }
231
232        // |y| > |1075 / log2(1 - 2^-53)|.
233        if y_a > 0x43d7_4910_d52d_3052 {
234            if y_a >= 0x7ff0_0000_0000_0000 {
235                // y is inf or nan
236                if y_mant != 0 {
237                    // y is NaN
238                    // pow(1, NaN) = 1
239                    // pow(x, NaN) = NaN
240                    return if x_u == 1f64.to_bits() { 1.0 } else { y };
241                }
242
243                // Now y is +-Inf
244                if f64::from_bits(x_abs).is_nan() {
245                    // pow(NaN, +-Inf) = NaN
246                    return x;
247                }
248
249                if x_a == 0x3ff0_0000_0000_0000 {
250                    // pow(+-1, +-Inf) = 1.0
251                    return 1.0;
252                }
253
254                if x == 0.0 && y_sign {
255                    // pow(+-0, -Inf) = +inf and raise FE_DIVBYZERO
256                    return f64::INFINITY;
257                }
258                // pow (|x| < 1, -inf) = +inf
259                // pow (|x| < 1, +inf) = 0.0
260                // pow (|x| > 1, -inf) = 0.0
261                // pow (|x| > 1, +inf) = +inf
262                return if (x_a < 1f64.to_bits()) == y_sign {
263                    f64::INFINITY
264                } else {
265                    0.0
266                };
267            }
268            // x^y will overflow / underflow in double precision.  Set y to a
269            // large enough exponent but not too large, so that the computations
270            // won't overflow in double precision.
271            y = if y_sign {
272                f64::from_bits(0xc630000000000000)
273            } else {
274                f64::from_bits(0x4630000000000000)
275            };
276        }
277
278        // y is finite and non-zero.
279
280        if x_u == 1f64.to_bits() {
281            // pow(1, y) = 1
282            return 1.0;
283        }
284
285        if x == 0.0 {
286            let out_is_neg = x_sign && is_odd_integer(y);
287            if y_sign {
288                // pow(0, negative number) = inf
289                return if out_is_neg {
290                    f64::NEG_INFINITY
291                } else {
292                    f64::INFINITY
293                };
294            }
295            // pow(0, positive number) = 0
296            return if out_is_neg { -0.0 } else { 0.0 };
297        } else if x == 2.0 {
298            return pow_exp2_fallback(y);
299        } else if x == 10.0 {
300            return pow_exp10_fallback(y);
301        }
302
303        if x_a == f64::INFINITY.to_bits() {
304            let out_is_neg = x_sign && is_odd_integer(y);
305            if y_sign {
306                return if out_is_neg { -0.0 } else { 0.0 };
307            }
308            return if out_is_neg {
309                f64::NEG_INFINITY
310            } else {
311                f64::INFINITY
312            };
313        }
314
315        if x_a > f64::INFINITY.to_bits() {
316            // x is NaN.
317            // pow (aNaN, 0) is already taken care above.
318            return x;
319        }
320
321        // x is finite and negative, and y is a finite integer.
322
323        let is_y_integer = is_integer(y);
324        // y is integer and in [-102;102] and |x|<2^10
325
326        // this is correct only for positive exponent number without FMA,
327        // otherwise reciprocal may overflow.
328        #[cfg(any(
329            all(
330                any(target_arch = "x86", target_arch = "x86_64"),
331                target_feature = "fma"
332            ),
333            all(target_arch = "aarch64", target_feature = "neon")
334        ))]
335        if is_y_integer
336            && y_a <= 0x4059800000000000u64
337            && x_a <= 0x4090000000000000u64
338            && x_a > 0x3cc0_0000_0000_0000
339        {
340            return f_powi(x, y);
341        }
342        #[cfg(not(any(
343            all(
344                any(target_arch = "x86", target_arch = "x86_64"),
345                target_feature = "fma"
346            ),
347            all(target_arch = "aarch64", target_feature = "neon")
348        )))]
349        if is_y_integer
350            && y_a <= 0x4059800000000000u64
351            && x_a <= 0x4090000000000000u64
352            && x_a > 0x3cc0_0000_0000_0000
353            && y.is_sign_positive()
354        {
355            return f_powi(x, y);
356        }
357
358        if x_sign {
359            if is_y_integer {
360                x = -x;
361                if is_odd_integer(y) {
362                    // sign = -1.0;
363                    static CS: [f64; 2] = [1.0, -1.0];
364
365                    // set sign to 1 for y even, to -1 for y odd
366                    let y_parity = if (y.abs()) >= f64::from_bits(0x4340000000000000) {
367                        0usize
368                    } else {
369                        (y as i64 & 0x1) as usize
370                    };
371                    s = CS[y_parity];
372                }
373            } else {
374                // pow( negative, non-integer ) = NaN
375                return f64::NAN;
376            }
377        }
378
379        // y is finite and non-zero.
380
381        if x_u == 1f64.to_bits() {
382            // pow(1, y) = 1
383            return 1.0;
384        }
385
386        if x == 0.0 {
387            let out_is_neg = x_sign && is_odd_integer(y);
388            if y_sign {
389                // pow(0, negative number) = inf
390                return if out_is_neg {
391                    f64::NEG_INFINITY
392                } else {
393                    f64::INFINITY
394                };
395            }
396            // pow(0, positive number) = 0
397            return if out_is_neg { -0.0 } else { 0.0 };
398        }
399
400        if x_a == f64::INFINITY.to_bits() {
401            let out_is_neg = x_sign && is_odd_integer(y);
402            if y_sign {
403                return if out_is_neg { -0.0 } else { 0.0 };
404            }
405            return if out_is_neg {
406                f64::NEG_INFINITY
407            } else {
408                f64::INFINITY
409            };
410        }
411
412        if x_a > f64::INFINITY.to_bits() {
413            // x is NaN.
414            // pow (aNaN, 0) is already taken care above.
415            return x;
416        }
417    }
418
419    // approximate log(x)
420    let (mut l, cancel) = pow_log_1(x);
421
422    /* We should avoid a spurious underflow/overflow in y*log(x).
423    Underflow: for x<>1, the smallest absolute value of log(x) is obtained
424    for x=1-2^-53, with |log(x)| ~ 2^-53. Thus to avoid a spurious underflow
425    we require |y| >= 2^-969.
426    Overflow: the largest absolute value of log(x) is obtained for x=2^-1074,
427    with |log(x)| < 745. Thus to avoid a spurious overflow we require
428    |y| < 2^1014. */
429    let ey = ((y.to_bits() >> 52) & 0x7ff) as i32;
430    if ey < 0x36 || ey >= 0x7f5 {
431        l.lo = f64::NAN;
432        l.hi = f64::NAN;
433    }
434
435    let r = DoubleDouble::quick_mult_f64(l, y);
436    let res = pow_exp_1(r, s);
437    static ERR: [u64; 2] = [0x3bf2700000000000, 0x3c55700000000000];
438    let res_min = res.hi + f_fmla(f64::from_bits(ERR[cancel as usize]), -res.hi, res.lo);
439    let res_max = res.hi + f_fmla(f64::from_bits(ERR[cancel as usize]), res.hi, res.lo);
440    if res_min == res_max {
441        return res_max;
442    }
443    pow_rational128(x, y, s)
444}
445
446#[cold]
447fn pow_rational128(x: f64, y: f64, s: f64) -> f64 {
448    /* the idea of returning res_max instead of res_min is due to Laurent
449    Théry: it is better in case of underflow since res_max = +0 always. */
450
451    let f_y = DyadicFloat128::new_from_f64(y);
452
453    let r = log_dyadic(x) * f_y;
454    let mut result = exp_dyadic(r);
455
456    // 2^R.ex <= R < 2^(R.ex+1)
457
458    // /* case R < 2^-1075: underflow case */
459    // if result.exponent < -1075 {
460    //     return 0.5 * (s * f64::from_bits(0x0000000000000001));
461    // }
462
463    result.sign = if s == -1.0 {
464        DyadicSign::Neg
465    } else {
466        DyadicSign::Pos
467    };
468
469    result.fast_as_f64()
470}
471
472/// Pow for given value for const context.
473/// This is simplified version just to make a good approximation on const context.
474#[inline]
475pub const fn pow(d: f64, n: f64) -> f64 {
476    let value = d.abs();
477
478    let r = n * log(value);
479    let c = exp(r);
480    if n == 0. {
481        return 1.;
482    }
483    if d < 0.0 {
484        let y = n as i32;
485        if y % 2 == 0 { c } else { -c }
486    } else {
487        c
488    }
489}
490
491#[cfg(test)]
492mod tests {
493    use super::*;
494
495    #[test]
496    fn powf_test() {
497        assert!(
498            (pow(2f64, 3f64) - 8f64).abs() < 1e-9,
499            "Invalid result {}",
500            pow(2f64, 3f64)
501        );
502        assert!(
503            (pow(0.5f64, 2f64) - 0.25f64).abs() < 1e-9,
504            "Invalid result {}",
505            pow(0.5f64, 2f64)
506        );
507    }
508
509    #[test]
510    fn f_pow_test() {
511        assert_eq!(f_pow(
512             0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000135264499699371,
513            -0.5,
514        ), 27189929701044785000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.);
515        assert_eq!(f_pow(
516            0.000000000000000000000000000000000000000000000000000021798599361155193,
517            -2.,
518        ),2104470396771397700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.);
519        assert_eq!(
520            f_pow(-25192281723900620000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.,
521            -2.),
522            0.
523        );
524        assert_eq!(
525            f_pow(0.000000000000000000000000000000000000000000000000000021799650661798696,
526                  -2.),
527            2104267423084451500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.
528        );
529        assert_eq!(
530            f_pow(0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014916691520383755,
531            -2.),
532            44942267764413600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.
533        );
534        assert_eq!(
535            f_pow(
536                0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000135264499699371,
537                -0.5,
538            ),
539            27189929701044785000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.
540        );
541        assert_eq!(f_pow(1., f64::INFINITY), 1.);
542        assert_eq!(f_pow(2., f64::INFINITY), f64::INFINITY);
543        assert_eq!(f_pow(f64::INFINITY, -0.5), 0.);
544        assert!(
545            (f_pow(2f64, 3f64) - 8f64).abs() < 1e-9,
546            "Invalid result {}",
547            f_pow(2f64, 3f64)
548        );
549        assert!(
550            (f_pow(0.5f64, 2f64) - 0.25f64).abs() < 1e-9,
551            "Invalid result {}",
552            f_pow(0.5f64, 2f64)
553        );
554        assert_eq!(f_pow(2.1f64, 2.7f64), 7.412967494768546);
555        assert_eq!(f_pow(27., 1. / 3.), 3.);
556    }
557
558    #[test]
559    fn powi_test() {
560        assert_eq!(f_pow(f64::from_bits(0x3cc0_0000_0000_0000), 102.), 0.0);
561        assert_eq!(f_pow(3., 3.), 27.);
562        assert_eq!(f_pow(3., -3.), 1. / 27.);
563        assert_eq!(f_pow(3., 102.), 4.638397686588102e48);
564        assert_eq!(f_pow(0.000000000000011074474670636028, -22.), 10589880229528372000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.);
565    }
566}