/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 | | } |