Coverage Report

Created: 2025-12-20 06:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/pxfm-0.1.27/src/compound/powm1.rs
Line
Count
Source
1
/*
2
 * // Copyright (c) Radzivon Bartoshyk 8/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::{is_integer, is_odd_integer};
30
use crate::double_double::DoubleDouble;
31
use crate::exponents::{EXPM1_T0, EXPM1_T1, ldexp};
32
use crate::pow_exec::pow_log_1;
33
use crate::rounding::CpuRoundTiesEven;
34
35
/// Computes x^y - 1
36
0
pub fn f_powm1(x: f64, y: f64) -> f64 {
37
0
    let ax: u64 = x.to_bits().wrapping_shl(1);
38
0
    let ay: u64 = y.to_bits().wrapping_shl(1);
39
40
    // filter out exceptional cases
41
0
    if ax == 0 || ax >= 0x7ffu64 << 53 || ay == 0 || ay >= 0x7ff64 << 53 {
42
0
        if x.is_nan() || y.is_nan() {
43
0
            return f64::NAN;
44
0
        }
45
46
        // Handle infinities
47
0
        if x.is_infinite() {
48
0
            return if x.is_sign_positive() {
49
0
                if y.is_infinite() {
50
0
                    return f64::INFINITY;
51
0
                } else if y > 0.0 {
52
0
                    f64::INFINITY // inf^positive -> inf
53
0
                } else if y < 0.0 {
54
0
                    -1.0 // inf^negative -> 0, so powm1 = -1
55
                } else {
56
0
                    f64::NAN // inf^0 -> NaN (0^0 conventionally 1, inf^0 = NaN)
57
                }
58
            } else {
59
                // x = -inf
60
0
                if y.is_infinite() {
61
0
                    return -1.0;
62
0
                }
63
0
                if is_integer(y) {
64
                    // Negative base: (-inf)^even = +inf, (-inf)^odd = -inf
65
0
                    let pow = if y as i32 % 2 == 0 {
66
0
                        f64::INFINITY
67
                    } else {
68
0
                        f64::NEG_INFINITY
69
                    };
70
0
                    pow - 1.0
71
                } else {
72
0
                    f64::NAN // Negative base with non-integer exponent
73
                }
74
            };
75
0
        }
76
77
        // Handle y infinite
78
0
        if y.is_infinite() {
79
0
            return if x.abs() > 1.0 {
80
0
                if y.is_sign_positive() {
81
0
                    f64::INFINITY
82
                } else {
83
0
                    -1.0
84
                }
85
0
            } else if x.abs() < 1.0 {
86
0
                if y.is_sign_positive() {
87
0
                    -1.0
88
                } else {
89
0
                    f64::INFINITY
90
                }
91
            } else {
92
                // |x| == 1
93
0
                f64::NAN // 1^inf or -1^inf is undefined
94
            };
95
0
        }
96
97
        // Handle zero base
98
0
        if x == 0.0 {
99
0
            return if y > 0.0 {
100
0
                -1.0 // 0^positive -> 0, powm1 = -1
101
0
            } else if y < 0.0 {
102
0
                f64::INFINITY // 0^negative -> inf
103
            } else {
104
0
                0.0 // 0^0 -> conventionally 1, powm1 = 0
105
            };
106
0
        }
107
0
    }
108
109
0
    let y_integer = is_integer(y);
110
111
0
    let mut negative_parity: bool = false;
112
113
0
    let mut x = x;
114
115
    // Handle negative base with non-integer exponent
116
0
    if x < 0.0 {
117
0
        if !y_integer {
118
0
            return f64::NAN; // x < 0 and non-integer y
119
0
        }
120
0
        x = x.abs();
121
0
        if is_odd_integer(y) {
122
0
            negative_parity = true;
123
0
        }
124
0
    }
125
126
0
    let (mut l, _) = pow_log_1(x);
127
0
    l = DoubleDouble::from_exact_add(l.hi, l.lo);
128
129
0
    let r = DoubleDouble::quick_mult_f64(l, y);
130
0
    if r.hi < -37.42994775023705 {
131
        // underflow
132
0
        return -1.;
133
0
    }
134
0
    let res = powm1_expm1_1(r);
135
    // For x < 0 and integer y = n:
136
    // if n is even: x^n = |x|^n → powm1 = |x|^n - 1 (same sign as res).
137
    // if n is odd: x^n = -|x|^n → powm1 = -|x|^n - 1 = - (|x|^n + 1).
138
0
    if negative_parity {
139
0
        DoubleDouble::full_add_f64(-res, -2.).to_f64()
140
    } else {
141
0
        res.to_f64()
142
    }
143
0
}
144
145
#[inline]
146
0
pub(crate) fn powm1_expm1_1(r: DoubleDouble) -> DoubleDouble {
147
0
    let ax = r.hi.to_bits() & 0x7fffffffffffffffu64;
148
149
    const LOG2H: f64 = f64::from_bits(0x3f262e42fefa39ef);
150
    const LOG2L: f64 = f64::from_bits(0x3bbabc9e3b39803f);
151
152
0
    if ax <= 0x3f80000000000000 {
153
        // |x| < 2^-7
154
0
        if ax < 0x3970000000000000 {
155
            // |x| < 2^-104
156
0
            return r;
157
0
        }
158
0
        let d = crate::pow_exec::expm1_poly_dd_tiny(r);
159
0
        return d;
160
0
    }
161
162
    const INVLOG2: f64 = f64::from_bits(0x40b71547652b82fe);
163
164
0
    let k = (r.hi * INVLOG2).cpu_round_ties_even();
165
166
0
    let z = DoubleDouble::mul_f64_add(DoubleDouble::new(LOG2L, LOG2H), -k, r);
167
168
0
    let bk = unsafe { k.to_int_unchecked::<i64>() }; /* Note: k is an integer, this is just a conversion. */
169
0
    let mk = (bk >> 12) + 0x3ff;
170
0
    let i2 = (bk >> 6) & 0x3f;
171
0
    let i1 = bk & 0x3f;
172
173
0
    let t0 = DoubleDouble::from_bit_pair(EXPM1_T0[i2 as usize]);
174
0
    let t1 = DoubleDouble::from_bit_pair(EXPM1_T1[i1 as usize]);
175
0
    let tbh = DoubleDouble::quick_mult(t1, t0);
176
0
    let mut de = tbh;
177
    // exp(k)=2^k*exp(r) + (2^k - 1)
178
0
    let q = crate::pow_exec::expm1_poly_fast(z);
179
0
    de = DoubleDouble::quick_mult(de, q);
180
0
    de = DoubleDouble::add(tbh, de);
181
182
0
    let ie = mk - 0x3ff;
183
184
0
    let off: f64 = f64::from_bits((2048i64 + 1023i64).wrapping_sub(ie).wrapping_shl(52) as u64);
185
186
    let e: f64;
187
0
    if ie < 53 {
188
0
        let fhz = DoubleDouble::from_exact_add(off, de.hi);
189
0
        de.hi = fhz.hi;
190
0
        e = fhz.lo;
191
0
    } else if ie < 104 {
192
0
        let fhz = DoubleDouble::from_exact_add(de.hi, off);
193
0
        de.hi = fhz.hi;
194
0
        e = fhz.lo;
195
0
    } else {
196
0
        e = 0.;
197
0
    }
198
0
    de.lo += e;
199
0
    de.hi = ldexp(de.to_f64(), ie as i32);
200
0
    de.lo = 0.;
201
0
    de
202
0
}
203
204
#[cfg(test)]
205
mod tests {
206
    use super::*;
207
    #[test]
208
    fn test_powm1() {
209
        assert_eq!(f_powm1(f64::INFINITY, f64::INFINITY), f64::INFINITY);
210
        assert_eq!(f_powm1(50850368932909610000000000., 0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023201985303960773), 1.3733470789307166e-303);
211
        assert_eq!(f_powm1(-3.375, -9671689000000000000000000.), -1.);
212
        assert_eq!(f_powm1(1.83329e-40, 2.4645883e-32), -2.255031542428047e-30);
213
        assert_eq!(f_powm1(3., 2.), 8.);
214
        assert_eq!(f_powm1(3., 3.), 26.);
215
        assert_eq!(f_powm1(5., 2.), 24.);
216
        assert_eq!(f_powm1(5., -2.), 1. / 25. - 1.);
217
        assert_eq!(f_powm1(-5., 2.), 24.);
218
        assert_eq!(f_powm1(-5., 3.), -126.);
219
        assert_eq!(
220
            f_powm1(196560., 0.000000000000000000000000000000000000001193773),
221
            1.4550568430468268e-38
222
        );
223
    }
224
}