Coverage Report

Created: 2026-01-10 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/pxfm-0.1.27/src/hyperbolic/asinh.rs
Line
Count
Source
1
/*
2
 * // Copyright (c) Radzivon Bartoshyk 7/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::{dd_fmla, f_fmla};
30
use crate::double_double::DoubleDouble;
31
use crate::hyperbolic::acosh::{
32
    ACOSH_ASINH_B, ACOSH_ASINH_L1, ACOSH_ASINH_L2, ACOSH_ASINH_LL, ACOSH_ASINH_R1, ACOSH_ASINH_R2,
33
    ACOSH_ASINH_REFINE_T2, ACOSH_ASINH_REFINE_T4, ACOSH_SINH_REFINE_T1, ACOSH_SINH_REFINE_T3,
34
    lpoly_xd_generic,
35
};
36
37
#[cold]
38
0
fn asinh_refine(x: f64, a: f64, z: DoubleDouble) -> f64 {
39
0
    let mut t = z.hi.to_bits();
40
0
    let ex: i32 = (t >> 52) as i32;
41
0
    let e = ex.wrapping_sub(0x3ff) + if z.lo == 0.0 { 1i32 } else { 0i32 };
42
0
    t &= 0x000fffffffffffff;
43
0
    t |= 0x3ffu64 << 52;
44
0
    let ed = e as f64;
45
0
    let v = (a - ed + f64::from_bits(0x3ff0000800000000)).to_bits();
46
0
    let i = (v.wrapping_sub(0x3ffu64 << 52)) >> (52 - 16);
47
0
    let i1 = (i >> 12) & 0x1f;
48
0
    let i2 = (i >> 8) & 0xf;
49
0
    let i3 = (i >> 4) & 0xf;
50
0
    let i4 = i & 0xf;
51
    const L20: f64 = f64::from_bits(0x3fd62e42fefa3800);
52
    const L21: f64 = f64::from_bits(0x3d1ef35793c76800);
53
    const L22: f64 = f64::from_bits(0xba49ff0342542fc3);
54
0
    let el2 = L22 * ed;
55
0
    let el1 = L21 * ed;
56
0
    let el0 = L20 * ed;
57
    let mut dl0: f64;
58
59
0
    let ll0i1 = ACOSH_ASINH_LL[0][i1 as usize];
60
0
    let ll1i2 = ACOSH_ASINH_LL[1][i2 as usize];
61
0
    let ll2i3 = ACOSH_ASINH_LL[2][i3 as usize];
62
0
    let ll3i4 = ACOSH_ASINH_LL[3][i4 as usize];
63
64
0
    dl0 = f64::from_bits(ll0i1.0)
65
0
        + f64::from_bits(ll1i2.0)
66
0
        + (f64::from_bits(ll2i3.0) + f64::from_bits(ll3i4.0));
67
0
    let dl1 = f64::from_bits(ll0i1.1)
68
0
        + f64::from_bits(ll1i2.1)
69
0
        + (f64::from_bits(ll2i3.1) + f64::from_bits(ll3i4.1));
70
0
    let dl2 = f64::from_bits(ll0i1.2)
71
0
        + f64::from_bits(ll1i2.2)
72
0
        + (f64::from_bits(ll2i3.2) + f64::from_bits(ll3i4.2));
73
0
    dl0 += el0;
74
0
    let t12 = f64::from_bits(ACOSH_SINH_REFINE_T1[i1 as usize])
75
0
        * f64::from_bits(ACOSH_ASINH_REFINE_T2[i2 as usize]);
76
0
    let t34 = f64::from_bits(ACOSH_SINH_REFINE_T3[i3 as usize])
77
0
        * f64::from_bits(ACOSH_ASINH_REFINE_T4[i4 as usize]);
78
0
    let th = t12 * t34;
79
0
    let tl = dd_fmla(t12, t34, -th);
80
0
    let dh = th * f64::from_bits(t);
81
0
    let dl = dd_fmla(th, f64::from_bits(t), -dh);
82
0
    let sh = tl * f64::from_bits(t);
83
0
    let sl = dd_fmla(tl, f64::from_bits(t), -sh);
84
85
0
    let mut dx = DoubleDouble::from_exact_add(dh - 1., dl);
86
0
    if z.lo != 0.0 {
87
0
        t = z.lo.to_bits();
88
0
        t = t.wrapping_sub((e as i64).wrapping_shl(52) as u64);
89
0
        dx.lo += th * f64::from_bits(t);
90
0
    }
91
0
    dx = DoubleDouble::add(dx, DoubleDouble::new(sl, sh));
92
    const CL: [u64; 3] = [0xbfc0000000000000, 0x3fb9999999a0754f, 0xbfb55555555c3157];
93
94
0
    let sl0 = f_fmla(dx.hi, f64::from_bits(CL[2]), f64::from_bits(CL[1]));
95
0
    let sl1 = f_fmla(dx.hi, sl0, f64::from_bits(CL[0]));
96
97
0
    let sl = dx.hi * sl1;
98
    const CH: [(u64, u64); 3] = [
99
        (0x39024b67ee516e3b, 0x3fe0000000000000),
100
        (0xb91932ce43199a8d, 0xbfd0000000000000),
101
        (0x3c655540c15cf91f, 0x3fc5555555555555),
102
    ];
103
0
    let mut s = lpoly_xd_generic(dx, CH, sl);
104
0
    s = DoubleDouble::quick_mult(dx, s);
105
0
    s = DoubleDouble::add(s, DoubleDouble::new(el2, el1));
106
0
    s = DoubleDouble::add(s, DoubleDouble::new(dl2, dl1));
107
0
    let mut v02 = DoubleDouble::from_exact_add(dl0, s.hi);
108
0
    let mut v12 = DoubleDouble::from_exact_add(v02.lo, s.lo);
109
0
    let scale = f64::copysign(2., x);
110
0
    v02.hi *= scale;
111
0
    v12.hi *= scale;
112
0
    v12.lo *= scale;
113
0
    t = v12.hi.to_bits();
114
0
    if (t & 0x000fffffffffffff) == 0 {
115
0
        let w = v12.lo.to_bits();
116
0
        if ((w ^ t) >> 63) != 0 {
117
0
            t = t.wrapping_sub(1);
118
0
        } else {
119
0
            t = t.wrapping_add(1);
120
0
        }
121
0
        v12.hi = f64::from_bits(t);
122
0
    }
123
0
    v02.hi + v12.hi
124
0
}
125
126
#[cold]
127
0
fn as_asinh_zero(x: f64, x2h: f64, x2l: f64) -> f64 {
128
    static CH: [(u64, u64); 12] = [
129
        (0xbc65555555555555, 0xbfc5555555555555),
130
        (0x3c499999999949df, 0x3fb3333333333333),
131
        (0x3c32492496091b0c, 0xbfa6db6db6db6db7),
132
        (0x3c1c71a35cfa0671, 0x3f9f1c71c71c71c7),
133
        (0x3c317f937248cf81, 0xbf96e8ba2e8ba2e9),
134
        (0xbc374e3c1dfd4c3d, 0x3f91c4ec4ec4ec4f),
135
        (0xbc238e7a467ecc55, 0xbf8c999999999977),
136
        (0x3c2a83c7bace55eb, 0x3f87a87878786c7e),
137
        (0xbc2d024df7fa0542, 0xbf83fde50d764083),
138
        (0xbc2ba9c13deb261f, 0x3f812ef3ceae4d12),
139
        (0xbc1546da9bc5b32a, 0xbf7df3bd104aa267),
140
        (0x3c140d284a1d67f9, 0x3f7a685fc5de7a04),
141
    ];
142
143
    const CL: [u64; 5] = [
144
        0xbf77828d553ec800,
145
        0x3f751712f7bee368,
146
        0xbf72e6d98527bcc6,
147
        0x3f70095da47b392c,
148
        0xbf63b92d6368192c,
149
    ];
150
151
0
    let yw0 = f_fmla(x2h, f64::from_bits(CL[4]), f64::from_bits(CL[3]));
152
0
    let yw1 = f_fmla(x2h, yw0, f64::from_bits(CL[2]));
153
0
    let yw2 = f_fmla(x2h, yw1, f64::from_bits(CL[1]));
154
155
0
    let y2 = x2h * f_fmla(x2h, yw2, f64::from_bits(CL[0]));
156
0
    let mut y1 = lpoly_xd_generic(DoubleDouble::new(x2l, x2h), CH, y2);
157
0
    y1 = DoubleDouble::quick_mult(y1, DoubleDouble::new(x2l, x2h));
158
0
    y1 = DoubleDouble::quick_mult_f64(y1, x);
159
160
0
    let y0 = DoubleDouble::from_exact_add(x, y1.hi);
161
0
    let mut p = DoubleDouble::from_exact_add(y0.lo, y1.lo);
162
163
0
    let mut t = p.hi.to_bits();
164
0
    if (t & 0x000fffffffffffff) == 0 {
165
0
        let w = p.lo.to_bits();
166
0
        if ((w ^ t) >> 63) != 0 {
167
0
            t = t.wrapping_sub(1);
168
0
        } else {
169
0
            t = t.wrapping_add(1);
170
0
        }
171
0
        p.hi = f64::from_bits(t);
172
0
    }
173
0
    y0.hi + p.hi
174
0
}
175
176
/// Huperbolic sine function
177
///
178
/// Max ULP 0.5
179
0
pub fn f_asinh(x: f64) -> f64 {
180
0
    let ax = x.abs();
181
0
    let ix = ax.to_bits();
182
0
    let u = ix;
183
0
    if u < 0x3fbb000000000000u64 {
184
        // |x| < 0x1.bp-4
185
        // for |x| < 0x1.7137449123ef7p-26, asinh(x) rounds to x to nearest
186
        // for |x| < 0x1p-1022 we have underflow but not for 0x1p-1022 (to nearest)
187
0
        if u < 0x3e57137449123ef7u64 {
188
            // |x| < 0x1.7137449123ef7p-26
189
0
            if u == 0 {
190
0
                return x;
191
0
            }
192
0
            let res = f_fmla(f64::from_bits(0xbc30000000000000), x, x);
193
0
            return res;
194
0
        }
195
0
        let x2h = x * x;
196
0
        let x2l = dd_fmla(x, x, -x2h);
197
0
        let x3h = x2h * x;
198
        let sl;
199
0
        if u < 0x3f93000000000000u64 {
200
            // |x| < 0x1.3p-6
201
0
            if u < 0x3f30000000000000u64 {
202
                // |x| < 0x1p-12
203
0
                if u < 0x3e5a000000000000u64 {
204
0
                    // |x| < 0x1.ap-26
205
0
                    sl = x3h * f64::from_bits(0xbfc5555555555555);
206
0
                } else {
207
                    const CL: [u64; 2] = [0xbfc5555555555555, 0x3fb3333327c57c60];
208
0
                    let sl0 = f_fmla(x2h, f64::from_bits(CL[1]), f64::from_bits(CL[0]));
209
0
                    sl = x3h * sl0;
210
                }
211
            } else {
212
                const CL: [u64; 4] = [
213
                    0xbfc5555555555555,
214
                    0x3fb333333332f2ff,
215
                    0xbfa6db6d9a665159,
216
                    0x3f9f186866d775f0,
217
                ];
218
0
                let sl0 = f_fmla(x2h, f64::from_bits(CL[3]), f64::from_bits(CL[2]));
219
0
                let sl1 = f_fmla(x2h, sl0, f64::from_bits(CL[1]));
220
0
                sl = x3h * f_fmla(x2h, sl1, f64::from_bits(CL[0]));
221
            }
222
        } else {
223
            const CL: [u64; 7] = [
224
                0xbfc5555555555555,
225
                0x3fb3333333333310,
226
                0xbfa6db6db6da466c,
227
                0x3f9f1c71c2ea7be4,
228
                0xbf96e8b651b09d72,
229
                0x3f91c309fc0e69c2,
230
                0xbf8bab7833c1e000,
231
            ];
232
0
            let c1 = f_fmla(x2h, f64::from_bits(CL[2]), f64::from_bits(CL[1]));
233
0
            let c3 = f_fmla(x2h, f64::from_bits(CL[4]), f64::from_bits(CL[3]));
234
0
            let c5 = f_fmla(x2h, f64::from_bits(CL[6]), f64::from_bits(CL[5]));
235
0
            let x4 = x2h * x2h;
236
237
0
            let sl0 = f_fmla(x4, c5, c3);
238
0
            let sl1 = f_fmla(x4, sl0, c1);
239
240
0
            sl = x3h * f_fmla(x2h, sl1, f64::from_bits(CL[0]));
241
        }
242
0
        let eps = f64::from_bits(0x3ca6000000000000) * x3h;
243
0
        let lb = x + (sl - eps);
244
0
        let ub = x + (sl + eps);
245
0
        if lb == ub {
246
0
            return lb;
247
0
        }
248
0
        return as_asinh_zero(x, x2h, x2l);
249
0
    }
250
251
    // |x| >= 0x1.bp-4
252
0
    let mut x2h: f64 = 0.;
253
0
    let mut x2l: f64 = 0.;
254
0
    let mut off: i32 = 0x3ff;
255
256
0
    let va: DoubleDouble = if u < 0x4190000000000000 {
257
        // x < 0x1p+26
258
0
        x2h = x * x;
259
0
        x2l = dd_fmla(x, x, -x2h);
260
0
        let mut dt = if u < 0x3ff0000000000000 {
261
0
            DoubleDouble::from_exact_add(1., x2h)
262
        } else {
263
0
            DoubleDouble::from_exact_add(x2h, 1.)
264
        };
265
0
        dt.lo += x2l;
266
267
0
        let ah = dt.hi.sqrt();
268
0
        let rs = 0.5 / dt.hi;
269
0
        let al = (dt.lo - dd_fmla(ah, ah, -dt.hi)) * (rs * ah);
270
0
        let mut ma = DoubleDouble::from_exact_add(ah, ax);
271
0
        ma.lo += al;
272
0
        ma
273
0
    } else if u < 0x4330000000000000 {
274
0
        DoubleDouble::new(0.5 / ax, 2. * ax)
275
    } else {
276
0
        if u >= 0x7ff0000000000000u64 {
277
0
            return x + x;
278
0
        } // +-inf or nan
279
0
        off = 0x3fe;
280
0
        DoubleDouble::new(0., ax)
281
    };
282
283
0
    let mut t = va.hi.to_bits();
284
0
    let ex = (t >> 52) as i32;
285
0
    let e = ex.wrapping_sub(off);
286
0
    t &= 0x000fffffffffffff;
287
0
    let ed = e as f64;
288
0
    let i = t >> (52 - 5);
289
0
    let d = (t & 0x00007fffffffffff) as i64;
290
0
    let b_i = ACOSH_ASINH_B[i as usize];
291
0
    let j: u64 = t
292
0
        .wrapping_add((b_i[0] as u64).wrapping_shl(33))
293
0
        .wrapping_add((b_i[1] as i64).wrapping_mul(d >> 16) as u64)
294
0
        >> (52 - 10);
295
0
    t |= 0x3ffu64 << 52;
296
0
    let i1 = (j >> 5) as i32;
297
0
    let i2 = j & 0x1f;
298
0
    let r =
299
0
        f64::from_bits(ACOSH_ASINH_R1[i1 as usize]) * f64::from_bits(ACOSH_ASINH_R2[i2 as usize]);
300
0
    let dx = dd_fmla(r, f64::from_bits(t), -1.);
301
0
    let dx2 = dx * dx;
302
    const C: [u64; 5] = [
303
        0xbfe0000000000000,
304
        0x3fd5555555555530,
305
        0xbfcfffffffffffa0,
306
        0x3fc99999e33a6366,
307
        0xbfc555559ef9525f,
308
    ];
309
310
0
    let fw0 = f_fmla(dx, f64::from_bits(C[3]), f64::from_bits(C[2]));
311
0
    let fw1 = f_fmla(dx, f64::from_bits(C[1]), f64::from_bits(C[0]));
312
0
    let fw2 = f_fmla(dx2, f64::from_bits(C[4]), fw0);
313
314
0
    let f = dx2 * f_fmla(dx2, fw2, fw1);
315
    const L2H: f64 = f64::from_bits(0x3fe62e42fefa3800);
316
    const L2L: f64 = f64::from_bits(0x3d2ef35793c76730);
317
0
    let l1i1 = ACOSH_ASINH_L1[i1 as usize];
318
0
    let l1i2 = ACOSH_ASINH_L2[i2 as usize];
319
0
    let mut lh = f_fmla(L2H, ed, f64::from_bits(l1i1.1) + f64::from_bits(l1i2.1));
320
0
    let mut ll = f_fmla(
321
        L2L,
322
0
        ed,
323
0
        f64::from_bits(l1i1.0) + f64::from_bits(l1i2.0) + va.lo / va.hi + f,
324
    );
325
0
    ll += dx;
326
0
    lh *= f64::copysign(1., x);
327
0
    ll *= f64::copysign(1., x);
328
0
    let eps = 1.63e-19;
329
0
    let lb = lh + (ll - eps);
330
0
    let ub = lh + (ll + eps);
331
0
    if lb == ub {
332
0
        return lb;
333
0
    }
334
0
    if ax < f64::from_bits(0x3fd0000000000000) {
335
0
        return as_asinh_zero(x, x2h, x2l);
336
0
    }
337
0
    asinh_refine(x, f64::from_bits(0x3ff71547652b82fe) * lb.abs(), va)
338
0
}
339
340
#[cfg(test)]
341
mod tests {
342
    use super::*;
343
344
    #[test]
345
    fn test_asinh() {
346
        assert_eq!(f_asinh(-0.05268859863273256), -0.05266425100170862);
347
        assert_eq!(f_asinh(1.05268859863273256), 0.9181436936151385);
348
    }
349
}