Coverage Report

Created: 2026-06-10 07:53

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/pxfm-0.1.29/src/hyperbolic/sinh.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::exponents::{EXP_REDUCE_T0, EXP_REDUCE_T1};
32
use crate::hyperbolic::acosh::lpoly_xd_generic;
33
34
#[cold]
35
0
pub(crate) fn hyperbolic_exp_accurate(x: f64, t: f64, zt: DoubleDouble) -> DoubleDouble {
36
    static CH: [(u64, u64); 3] = [
37
        (0x3a16c16bd194535d, 0x3ff0000000000000),
38
        (0xba28259d904fd34f, 0x3fe0000000000000),
39
        (0x3c653e93e9f26e62, 0x3fc5555555555555),
40
    ];
41
    const L2H: f64 = f64::from_bits(0x3f262e42ff000000);
42
    const L2L: f64 = f64::from_bits(0x3d0718432a1b0e26);
43
    const L2LL: f64 = f64::from_bits(0x3999ff0342542fc3);
44
0
    let dx = x - L2H * t;
45
0
    let mut dxl = L2L * t;
46
0
    let dxll = f_fmla(L2LL, t, dd_fmla(L2L, t, -dxl));
47
0
    let dxh = dx + dxl;
48
0
    dxl = ((dx - dxh) + dxl) + dxll;
49
50
0
    let fl0 = f_fmla(
51
0
        dxh,
52
0
        f64::from_bits(0x3f56c16c169400a7),
53
0
        f64::from_bits(0x3f811111113e93e9),
54
    );
55
56
0
    let fl = dxh * f_fmla(dxh, fl0, f64::from_bits(0x3fa5555555555555));
57
0
    let mut f = lpoly_xd_generic(DoubleDouble::new(dxl, dxh), CH, fl);
58
0
    f = DoubleDouble::quick_mult(DoubleDouble::new(dxl, dxh), f);
59
0
    f = DoubleDouble::quick_mult(zt, f);
60
0
    let zh = zt.hi + f.hi;
61
0
    let zl = (zt.hi - zh) + f.hi;
62
0
    let uh = zh + zt.lo;
63
0
    let ul = ((zh - uh) + zt.lo) + zl;
64
0
    let vh = uh + f.lo;
65
0
    let vl = ((uh - vh) + f.lo) + ul;
66
0
    DoubleDouble::new(vl, vh)
67
0
}
68
69
#[cold]
70
0
fn as_sinh_zero(x: f64) -> f64 {
71
    static CH: [(u64, u64); 5] = [
72
        (0x3c6555555555552f, 0x3fc5555555555555),
73
        (0x3c011111115cf00d, 0x3f81111111111111),
74
        (0x3b6a0011c925b85c, 0x3f2a01a01a01a01a),
75
        (0xbb6b4e2835532bcd, 0x3ec71de3a556c734),
76
        (0xbaedefcf17a6ab79, 0x3e5ae64567f54482),
77
    ];
78
0
    let d2x = DoubleDouble::from_exact_mult(x, x);
79
80
0
    let yw0 = f_fmla(
81
0
        d2x.hi,
82
0
        f64::from_bits(0x3ce95785063cd974),
83
0
        f64::from_bits(0x3d6ae7f36beea815),
84
    );
85
86
0
    let y2 = d2x.hi * f_fmla(d2x.hi, yw0, f64::from_bits(0x3de6124613aef206));
87
0
    let mut y1 = lpoly_xd_generic(d2x, CH, y2);
88
0
    y1 = DoubleDouble::quick_mult_f64(y1, x);
89
0
    y1 = DoubleDouble::quick_mult(y1, d2x); // y2 = y1.l
90
0
    let y0 = DoubleDouble::from_exact_add(x, y1.hi); // y0 = y0.hi
91
0
    let mut p = DoubleDouble::from_exact_add(y0.lo, y1.lo);
92
0
    let mut t = p.hi.to_bits();
93
0
    if (t & 0x000fffffffffffff) == 0 {
94
0
        let w = p.lo.to_bits();
95
0
        if ((w ^ t) >> 63) != 0 {
96
0
            t = t.wrapping_sub(1);
97
0
        } else {
98
0
            t = t.wrapping_add(1);
99
0
        }
100
0
        p.hi = f64::from_bits(t);
101
0
    }
102
0
    y0.hi + p.hi
103
0
}
104
105
/// Hyperbolic sine function
106
///
107
/// Max ULP 0.5
108
0
pub fn f_sinh(x: f64) -> f64 {
109
    /*
110
     The function sinh(x) is approximated by a minimax polynomial for
111
     |x|<0.25. For other arguments the identity
112
     sinh(x)=(exp(|x|)-exp(-|x|))/2*copysign(1,x) is used. For |x|<5
113
     both exponents are calculated with slightly higher precision than
114
     double. For 5<|x|<36.736801 the exp(-|x|) is small and is
115
     calculated with double precision but exp(|x|) is calculated with
116
     higher than double precision. For 36.736801<|x|<710.47586
117
     exp(-|x|) becomes too small and only exp(|x|) is calculated.
118
    */
119
120
    const S: f64 = f64::from_bits(0x40b71547652b82fe);
121
0
    let ax = x.abs();
122
0
    let v0 = dd_fmla(ax, S, f64::from_bits(0x4198000002000000));
123
0
    let jt = v0.to_bits();
124
0
    let v = v0.to_bits() & 0xfffffffffc000000;
125
0
    let t = f64::from_bits(v) - f64::from_bits(0x4198000000000000);
126
0
    let ix = ax.to_bits();
127
0
    let aix = ix;
128
0
    if aix < 0x3fd0000000000000u64 {
129
        // |x| < 0x1p-2
130
0
        if aix < 0x3e57137449123ef7u64 {
131
            // |x| < 0x1.7137449123ef7p-26
132
            /* We have underflow exactly when 0 < |x| < 2^-1022:
133
            for RNDU, sinh(2^-1022-2^-1074) would round to 2^-1022-2^-1075
134
            with unbounded exponent range */
135
0
            return dd_fmla(x, f64::from_bits(0x3c80000000000000), x);
136
0
        }
137
        const C: [u64; 5] = [
138
            0x3fc5555555555555,
139
            0x3f81111111111087,
140
            0x3f2a01a01a12e1c3,
141
            0x3ec71de2e415aa36,
142
            0x3e5aed2bff4269e6,
143
        ];
144
0
        let x2 = x * x;
145
0
        let x3 = x2 * x;
146
0
        let x4 = x2 * x2;
147
148
0
        let pw0 = f_fmla(x2, f64::from_bits(C[3]), f64::from_bits(C[2]));
149
0
        let pw1 = f_fmla(x2, f64::from_bits(C[1]), f64::from_bits(C[0]));
150
0
        let pw2 = f_fmla(x4, f64::from_bits(C[4]), pw0);
151
152
0
        let p = x3 * f_fmla(x4, pw2, pw1);
153
0
        let e = x3 * f64::from_bits(0x3ca9000000000000);
154
0
        let lb = x + (p - e);
155
0
        let ub = x + (p + e);
156
0
        if lb == ub {
157
0
            return lb;
158
0
        }
159
0
        return as_sinh_zero(x);
160
0
    }
161
162
0
    if aix > 0x408633ce8fb9f87du64 {
163
        // |x| >~ 710.47586
164
0
        if aix >= 0x7ff0000000000000u64 {
165
0
            return x + x;
166
0
        } // nan Inf
167
0
        return f64::copysign(f64::from_bits(0x7fe0000000000000), x) * 2.0;
168
0
    }
169
0
    let il: i64 = ((jt.wrapping_shl(14)) >> 40) as i64;
170
0
    let jl = -il;
171
0
    let i1 = il & 0x3f;
172
0
    let i0 = (il >> 6) & 0x3f;
173
0
    let ie = il >> 12;
174
0
    let j1 = jl & 0x3f;
175
0
    let j0 = (jl >> 6) & 0x3f;
176
0
    let je = jl >> 12;
177
0
    let mut sp = (1022i64.wrapping_add(ie) as u64).wrapping_shl(52);
178
0
    let sm = (1022i64.wrapping_add(je) as u64).wrapping_shl(52);
179
180
0
    let sn0 = EXP_REDUCE_T0[i0 as usize];
181
0
    let sn1 = EXP_REDUCE_T1[i1 as usize];
182
0
    let t0h = f64::from_bits(sn0.1);
183
0
    let t0l = f64::from_bits(sn0.0);
184
0
    let t1h = f64::from_bits(sn1.1);
185
0
    let t1l = f64::from_bits(sn1.0);
186
0
    let mut th = t0h * t1h;
187
0
    let mut tl = f_fmla(t0h, t1l, t1h * t0l) + dd_fmla(t0h, t1h, -th);
188
    const L2H: f64 = f64::from_bits(0x3f262e42ff000000);
189
    const L2L: f64 = f64::from_bits(0x3d0718432a1b0e26);
190
0
    let dx = f_fmla(L2L, t, f_fmla(-L2H, t, ax));
191
0
    let dx2 = dx * dx;
192
0
    let mx = -dx;
193
    const CH: [u64; 4] = [
194
        0x3ff0000000000000,
195
        0x3fe0000000000000,
196
        0x3fc5555555aaaaae,
197
        0x3fa55555551c98c0,
198
    ];
199
200
    let (mut rl, mut rh);
201
202
0
    let pp0 = f_fmla(dx, f64::from_bits(CH[3]), f64::from_bits(CH[2]));
203
0
    let pp1 = f_fmla(dx, f64::from_bits(CH[1]), f64::from_bits(CH[0]));
204
205
0
    let pp = dx * f_fmla(dx2, pp0, pp1);
206
0
    if aix > 0x4014000000000000u64 {
207
        // |x| > 5
208
0
        if aix > 0x40425e4f7b2737fau64 {
209
            // |x| >~ 36.736801
210
0
            sp = (1021i64.wrapping_add(ie) as u64).wrapping_shl(52);
211
0
            let mut rh = th;
212
0
            let mut rl = tl + th * pp;
213
0
            rh *= f64::copysign(1., x);
214
0
            rl *= f64::copysign(1., x);
215
0
            let e = 0.11e-18 * th;
216
0
            let lb = rh + (rl - e);
217
0
            let ub = rh + (rl + e);
218
0
            if lb == ub {
219
0
                return (lb * f64::from_bits(sp)) * 2.;
220
0
            }
221
0
            let mut tt = hyperbolic_exp_accurate(ax, t, DoubleDouble::new(tl, th));
222
0
            tt = DoubleDouble::from_exact_add(tt.hi, tt.lo);
223
0
            th = tt.hi;
224
0
            tl = tt.lo;
225
0
            th *= f64::copysign(1., x);
226
0
            tl *= f64::copysign(1., x);
227
0
            th += tl;
228
0
            th *= 2.;
229
0
            th *= f64::from_bits(sp);
230
0
            return th;
231
0
        }
232
233
0
        let q0h = f64::from_bits(EXP_REDUCE_T0[j0 as usize].1);
234
0
        let q1h = f64::from_bits(EXP_REDUCE_T1[j1 as usize].1);
235
0
        let mut qh = q0h * q1h;
236
0
        th *= f64::from_bits(sp);
237
0
        tl *= f64::from_bits(sp);
238
0
        qh *= f64::from_bits(sm);
239
240
0
        let pm0 = f_fmla(mx, f64::from_bits(CH[3]), f64::from_bits(CH[2]));
241
0
        let pm1 = f_fmla(mx, f64::from_bits(CH[1]), f64::from_bits(CH[0]));
242
243
0
        let pm = mx * f_fmla(dx2, pm0, pm1);
244
0
        let em = f_fmla(qh, pm, qh);
245
0
        rh = th;
246
0
        rl = f_fmla(th, pp, tl - em);
247
248
0
        rh *= f64::copysign(1., x);
249
0
        rl *= f64::copysign(1., x);
250
0
        let e = 0.09e-18 * rh;
251
0
        let lb = rh + (rl - e);
252
0
        let ub = rh + (rl + e);
253
0
        if lb == ub {
254
0
            return lb;
255
0
        }
256
257
0
        let tt = hyperbolic_exp_accurate(ax, t, DoubleDouble::new(tl, th));
258
0
        th = tt.hi;
259
0
        tl = tt.lo;
260
0
        if aix > 0x403f666666666666u64 {
261
0
            rh = th - qh;
262
0
            rl = ((th - rh) - qh) + tl;
263
0
        } else {
264
0
            qh = q0h * q1h;
265
0
            let q0l = f64::from_bits(EXP_REDUCE_T0[j0 as usize].0);
266
0
            let q1l = f64::from_bits(EXP_REDUCE_T1[j1 as usize].0);
267
0
            let mut ql = f_fmla(q0h, q1l, q1h * q0l) + dd_fmla(q0h, q1h, -qh);
268
0
            qh *= f64::from_bits(sm);
269
0
            ql *= f64::from_bits(sm);
270
0
            let qq = hyperbolic_exp_accurate(-ax, -t, DoubleDouble::new(ql, qh));
271
0
            rh = th - qq.hi;
272
0
            rl = (((th - rh) - qq.hi) - qq.lo) + tl;
273
0
        }
274
    } else {
275
0
        let tq0 = EXP_REDUCE_T0[j0 as usize];
276
0
        let tq1 = EXP_REDUCE_T1[j1 as usize];
277
0
        let q0h = f64::from_bits(tq0.1);
278
0
        let q0l = f64::from_bits(tq0.0);
279
0
        let q1h = f64::from_bits(tq1.1);
280
0
        let q1l = f64::from_bits(tq1.0);
281
0
        let mut qh = q0h * q1h;
282
0
        let mut ql = f_fmla(q0h, q1l, q1h * q0l) + dd_fmla(q0h, q1h, -qh);
283
0
        th *= f64::from_bits(sp);
284
0
        tl *= f64::from_bits(sp);
285
0
        qh *= f64::from_bits(sm);
286
0
        ql *= f64::from_bits(sm);
287
288
0
        let pm0 = f_fmla(mx, f64::from_bits(CH[3]), f64::from_bits(CH[2]));
289
0
        let pm1 = f_fmla(mx, f64::from_bits(CH[1]), f64::from_bits(CH[0]));
290
291
0
        let pm = mx * f_fmla(dx2, pm0, pm1);
292
0
        let fph = th;
293
0
        let fpl = f_fmla(th, pp, tl);
294
0
        let fmh = qh;
295
0
        let fml = f_fmla(qh, pm, ql);
296
297
0
        rh = fph - fmh;
298
0
        rl = ((fph - rh) - fmh) - fml + fpl;
299
0
        rh *= f64::copysign(1., x);
300
0
        rl *= f64::copysign(1., x);
301
0
        let e = 0.28e-18 * rh;
302
0
        let lb = rh + (rl - e);
303
0
        let ub = rh + (rl + e);
304
0
        if lb == ub {
305
0
            return lb;
306
0
        }
307
0
        let tt = hyperbolic_exp_accurate(ax, t, DoubleDouble::new(tl, th));
308
0
        let qq = hyperbolic_exp_accurate(-ax, -t, DoubleDouble::new(ql, qh));
309
0
        rh = tt.hi - qq.hi;
310
0
        rl = ((tt.hi - rh) - qq.hi) - qq.lo + tt.lo;
311
    }
312
0
    let r = DoubleDouble::from_exact_add(rh, rl);
313
0
    rh = r.hi;
314
0
    rl = r.lo;
315
0
    rh *= f64::copysign(1., x);
316
0
    rl *= f64::copysign(1., x);
317
0
    rh += rl;
318
0
    rh
319
0
}
320
321
#[cfg(test)]
322
mod tests {
323
    use super::*;
324
325
    #[test]
326
    fn test_f_sinh() {
327
        assert_eq!(f_sinh(1.), 1.1752011936438014);
328
    }
329
}