/rust/registry/src/index.crates.io-1949cf8c6b5b557f/pxfm-0.1.25/src/hyperbolic/cosh.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 | | use crate::hyperbolic::sinh::hyperbolic_exp_accurate; |
34 | | |
35 | | #[cold] |
36 | 0 | fn as_cosh_zero(x: f64) -> f64 { |
37 | | static CH: [(u64, u64); 4] = [ |
38 | | (0xb90c7e8db669f624, 0x3fe0000000000000), |
39 | | (0x3c45555555556135, 0x3fa5555555555555), |
40 | | (0xbbef49f4a6e838f2, 0x3f56c16c16c16c17), |
41 | | (0x3b3a4ffbe15316aa, 0x3efa01a01a01a01a), |
42 | | ]; |
43 | | const CL: [u64; 4] = [ |
44 | | 0x3e927e4fb7789f5c, |
45 | | 0x3e21eed8eff9089c, |
46 | | 0x3da939749ce13dad, |
47 | | 0x3d2ae9891efb6691, |
48 | | ]; |
49 | | |
50 | 0 | let dx2 = DoubleDouble::from_exact_mult(x, x); |
51 | | |
52 | 0 | let yw0 = f_fmla(dx2.hi, f64::from_bits(CL[3]), f64::from_bits(CL[2])); |
53 | 0 | let yw1 = f_fmla(dx2.hi, yw0, f64::from_bits(CL[1])); |
54 | | |
55 | 0 | let y2 = dx2.hi * f_fmla(dx2.hi, yw1, f64::from_bits(CL[0])); |
56 | | |
57 | 0 | let mut y1 = lpoly_xd_generic(dx2, CH, y2); |
58 | 0 | y1 = DoubleDouble::quick_mult(y1, dx2); // y2 = y1.l |
59 | 0 | let y0 = DoubleDouble::from_exact_add(1.0, y1.hi); // y0 = y0.hi |
60 | 0 | let mut p = DoubleDouble::from_exact_add(y0.lo, y1.lo); |
61 | 0 | let mut t = p.hi.to_bits(); |
62 | 0 | if (t & 0x000fffffffffffff) == 0 { |
63 | 0 | let w = p.lo.to_bits(); |
64 | 0 | if ((w ^ t) >> 63) != 0 { |
65 | 0 | t = t.wrapping_sub(1); |
66 | 0 | } else { |
67 | 0 | t = t.wrapping_add(1); |
68 | 0 | } |
69 | 0 | p.hi = f64::from_bits(t); |
70 | 0 | } |
71 | 0 | y0.hi + p.hi |
72 | 0 | } |
73 | | |
74 | | /// Hyperbolic cosine function |
75 | | /// |
76 | | /// Max ULP 0.5 |
77 | 0 | pub fn f_cosh(x: f64) -> f64 { |
78 | | /* |
79 | | The function sinh(x) is approximated by a minimax polynomial |
80 | | cosh(x)~1+x^2*P(x^2) for |x|<0.125. For other arguments the |
81 | | identity cosh(x)=(exp(|x|)+exp(-|x|))/2 is used. For |x|<5 both |
82 | | exponents are calculated with slightly higher precision than |
83 | | double. For 5<|x|<36.736801 the exp(-|x|) is rather small and is |
84 | | calculated with double precision but exp(|x|) is calculated with |
85 | | higher than double precision. For 36.736801<|x|<710.47586 |
86 | | exp(-|x|) becomes too small and only exp(|x|) is calculated. |
87 | | */ |
88 | | |
89 | | const S: f64 = f64::from_bits(0x40b71547652b82fe); |
90 | 0 | let ax = x.abs(); |
91 | 0 | let v0 = f_fmla(ax, S, f64::from_bits(0x4198000002000000)); |
92 | 0 | let jt = v0.to_bits(); |
93 | 0 | let mut v = v0.to_bits(); |
94 | 0 | v &= 0xfffffffffc000000; |
95 | 0 | let t = f64::from_bits(v) - f64::from_bits(0x4198000000000000); |
96 | 0 | let ix = ax.to_bits(); |
97 | 0 | let aix = ix; |
98 | 0 | if aix < 0x3fc0000000000000u64 { |
99 | 0 | if aix < 0x3e50000000000000u64 { |
100 | 0 | return f_fmla(ax, f64::from_bits(0x3c80000000000000), 1.); |
101 | 0 | } |
102 | | const C: [u64; 5] = [ |
103 | | 0x3fe0000000000000, |
104 | | 0x3fa555555555554e, |
105 | | 0x3f56c16c16c26737, |
106 | | 0x3efa019ffbbcdbda, |
107 | | 0x3e927ffe2df106cb, |
108 | | ]; |
109 | 0 | let x2 = x * x; |
110 | 0 | let x4 = x2 * x2; |
111 | | |
112 | 0 | let p0 = f_fmla(x2, f64::from_bits(C[3]), f64::from_bits(C[2])); |
113 | 0 | let p1 = f_fmla(x2, f64::from_bits(C[1]), f64::from_bits(C[0])); |
114 | 0 | let p2 = f_fmla(x4, f64::from_bits(C[4]), p0); |
115 | | |
116 | 0 | let p = x2 * f_fmla(x4, p2, p1); |
117 | 0 | let e = x2 * (4. * f64::from_bits(0x3ca0000000000000)); |
118 | 0 | let lb = 1. + (p - e); |
119 | 0 | let ub = 1. + (p + e); |
120 | 0 | if lb == ub { |
121 | 0 | return lb; |
122 | 0 | } |
123 | 0 | return as_cosh_zero(x); |
124 | 0 | } |
125 | | |
126 | | // treat large values apart to avoid a spurious invalid exception |
127 | 0 | if aix > 0x408633ce8fb9f87du64 { |
128 | | // |x| > 0x1.633ce8fb9f87dp+9 |
129 | 0 | if aix > 0x7ff0000000000000u64 { |
130 | 0 | return x + x; |
131 | 0 | } // nan |
132 | 0 | if aix == 0x7ff0000000000000u64 { |
133 | 0 | return x.abs(); |
134 | 0 | } // inf |
135 | 0 | return f64::from_bits(0x7fe0000000000000) * 2.0; |
136 | 0 | } |
137 | | |
138 | 0 | let il: i64 = ((jt.wrapping_shl(14)) >> 40) as i64; |
139 | 0 | let jl: i64 = -il; |
140 | 0 | let i1 = il & 0x3f; |
141 | 0 | let i0 = (il >> 6) & 0x3f; |
142 | 0 | let ie = il >> 12; |
143 | 0 | let j1 = jl & 0x3f; |
144 | 0 | let j0 = (jl >> 6) & 0x3f; |
145 | 0 | let je = jl >> 12; |
146 | 0 | let mut sp = (1022i64.wrapping_add(ie) as u64).wrapping_shl(52); |
147 | 0 | let sm = (1022i64.wrapping_add(je) as u64).wrapping_shl(52); |
148 | 0 | let sn0 = EXP_REDUCE_T0[i0 as usize]; |
149 | 0 | let sn1 = EXP_REDUCE_T1[i1 as usize]; |
150 | 0 | let t0h = f64::from_bits(sn0.1); |
151 | 0 | let t0l = f64::from_bits(sn0.0); |
152 | 0 | let t1h = f64::from_bits(sn1.1); |
153 | 0 | let t1l = f64::from_bits(sn1.0); |
154 | 0 | let mut th = t0h * t1h; |
155 | 0 | let mut tl = f_fmla(t0h, t1l, t1h * t0l) + dd_fmla(t0h, t1h, -th); |
156 | | |
157 | | const L2H: f64 = f64::from_bits(0x3f262e42ff000000); |
158 | | const L2L: f64 = f64::from_bits(0x3d0718432a1b0e26); |
159 | 0 | let dx = f_fmla(L2L, t, f_fmla(-L2H, t, ax)); |
160 | 0 | let dx2 = dx * dx; |
161 | 0 | let mx = -dx; |
162 | | const CH: [u64; 4] = [ |
163 | | 0x3ff0000000000000, |
164 | | 0x3fe0000000000000, |
165 | | 0x3fc5555555aaaaae, |
166 | | 0x3fa55555551c98c0, |
167 | | ]; |
168 | | |
169 | 0 | let pw0 = f_fmla(dx, f64::from_bits(CH[3]), f64::from_bits(CH[2])); |
170 | 0 | let pw1 = f_fmla(dx, f64::from_bits(CH[1]), f64::from_bits(CH[0])); |
171 | | |
172 | 0 | let pp = dx * f_fmla(dx2, pw0, pw1); |
173 | | let (mut rh, mut rl); |
174 | 0 | if aix > 0x4014000000000000u64 { |
175 | | // |x| > 5 |
176 | 0 | if aix > 0x40425e4f7b2737fau64 { |
177 | | // |x| >~ 36.736801 |
178 | 0 | sp = (1021i64.wrapping_add(ie) as u64).wrapping_shl(52); |
179 | 0 | rh = th; |
180 | 0 | rl = f_fmla(th, pp, tl); |
181 | 0 | let e = 0.11e-18 * th; |
182 | 0 | let lb = rh + (rl - e); |
183 | 0 | let ub = rh + (rl + e); |
184 | 0 | if lb == ub { |
185 | 0 | return (lb * f64::from_bits(sp)) * 2.; |
186 | 0 | } |
187 | | |
188 | 0 | let mut tt = hyperbolic_exp_accurate(ax, t, DoubleDouble::new(tl, th)); |
189 | 0 | tt = DoubleDouble::from_exact_add(tt.hi, tt.lo); |
190 | 0 | th = tt.hi; |
191 | 0 | tl = tt.lo; |
192 | 0 | th += tl; |
193 | 0 | th *= 2.; |
194 | 0 | th *= f64::from_bits(sp); |
195 | 0 | return th; |
196 | 0 | } |
197 | | |
198 | 0 | let q0h = f64::from_bits(EXP_REDUCE_T0[j0 as usize].1); |
199 | 0 | let q1h = f64::from_bits(EXP_REDUCE_T1[j1 as usize].1); |
200 | 0 | let mut qh = q0h * q1h; |
201 | 0 | th *= f64::from_bits(sp); |
202 | 0 | tl *= f64::from_bits(sp); |
203 | 0 | qh *= f64::from_bits(sm); |
204 | | |
205 | 0 | let pmw0 = f_fmla(mx, f64::from_bits(CH[3]), f64::from_bits(CH[2])); |
206 | 0 | let pmw1 = f_fmla(mx, f64::from_bits(CH[1]), f64::from_bits(CH[0])); |
207 | | |
208 | 0 | let pm = mx * f_fmla(dx2, pmw0, pmw1); |
209 | 0 | let em = f_fmla(qh, pm, qh); |
210 | 0 | rh = th; |
211 | 0 | rl = f_fmla(th, pp, tl + em); |
212 | 0 | let e = 0.09e-18 * rh; |
213 | 0 | let lb = rh + (rl - e); |
214 | 0 | let ub = rh + (rl + e); |
215 | 0 | if lb == ub { |
216 | 0 | return lb; |
217 | 0 | } |
218 | 0 | let tt = hyperbolic_exp_accurate(ax, t, DoubleDouble::new(tl, th)); |
219 | 0 | th = tt.hi; |
220 | 0 | tl = tt.lo; |
221 | 0 | if aix > 0x403f666666666666u64 { |
222 | 0 | rh = th + qh; |
223 | 0 | rl = ((th - rh) + qh) + tl; |
224 | 0 | } else { |
225 | 0 | qh = q0h * q1h; |
226 | 0 | let q0l = f64::from_bits(EXP_REDUCE_T0[j0 as usize].0); |
227 | 0 | let q1l = f64::from_bits(EXP_REDUCE_T1[j1 as usize].0); |
228 | 0 | let mut ql = f_fmla(q0h, q1l, q1h * q0l) + dd_fmla(q0h, q1h, -qh); |
229 | 0 | qh *= f64::from_bits(sm); |
230 | 0 | ql *= f64::from_bits(sm); |
231 | 0 | let qq = hyperbolic_exp_accurate(-ax, -t, DoubleDouble::new(ql, qh)); |
232 | 0 | qh = qq.hi; |
233 | 0 | ql = qq.lo; |
234 | 0 | rh = th + qh; |
235 | 0 | rl = (((th - rh) + qh) + ql) + tl; |
236 | 0 | } |
237 | | } else { |
238 | 0 | let tq0 = EXP_REDUCE_T0[j0 as usize]; |
239 | 0 | let tq1 = EXP_REDUCE_T1[j1 as usize]; |
240 | 0 | let q0h = f64::from_bits(tq0.1); |
241 | 0 | let q0l = f64::from_bits(tq0.0); |
242 | 0 | let q1h = f64::from_bits(tq1.1); |
243 | 0 | let q1l = f64::from_bits(tq1.0); |
244 | 0 | let mut qh = q0h * q1h; |
245 | 0 | let mut ql = f_fmla(q0h, q1l, q1h * q0l) + dd_fmla(q0h, q1h, -qh); |
246 | 0 | th *= f64::from_bits(sp); |
247 | 0 | tl *= f64::from_bits(sp); |
248 | 0 | qh *= f64::from_bits(sm); |
249 | 0 | ql *= f64::from_bits(sm); |
250 | | |
251 | 0 | let pmw0 = f_fmla(mx, f64::from_bits(CH[3]), f64::from_bits(CH[2])); |
252 | 0 | let pmw1 = f_fmla(mx, f64::from_bits(CH[1]), f64::from_bits(CH[0])); |
253 | | |
254 | 0 | let pm = mx * f_fmla(dx2, pmw0, pmw1); |
255 | 0 | let fph = th; |
256 | 0 | let fpl = f_fmla(th, pp, tl); |
257 | 0 | let fmh = qh; |
258 | 0 | let fml = f_fmla(qh, pm, ql); |
259 | | |
260 | 0 | rh = fph + fmh; |
261 | 0 | rl = ((fph - rh) + fmh) + fml + fpl; |
262 | 0 | let e = 0.28e-18 * rh; |
263 | 0 | let lb = rh + (rl - e); |
264 | 0 | let ub = rh + (rl + e); |
265 | 0 | if lb == ub { |
266 | 0 | return lb; |
267 | 0 | } |
268 | 0 | let tt = hyperbolic_exp_accurate(ax, t, DoubleDouble::new(tl, th)); |
269 | 0 | let qq = hyperbolic_exp_accurate(-ax, -t, DoubleDouble::new(ql, qh)); |
270 | 0 | rh = tt.hi + qq.hi; |
271 | 0 | rl = ((tt.hi - rh) + qq.hi) + qq.lo + tt.lo; |
272 | | } |
273 | 0 | let r = DoubleDouble::from_exact_add(rh, rl); |
274 | 0 | rh = r.hi; |
275 | 0 | rl = r.lo; |
276 | 0 | rh += rl; |
277 | 0 | rh |
278 | 0 | } |
279 | | |
280 | | #[cfg(test)] |
281 | | mod tests { |
282 | | |
283 | | use super::*; |
284 | | |
285 | | #[test] |
286 | | fn test_cosh() { |
287 | | assert_eq!(f_cosh(1.), 1.5430806348152437); |
288 | | assert_eq!(f_cosh(1.5454354343), 2.451616191647056); |
289 | | assert_eq!(f_cosh(15.5454354343), 2820115.088877147); |
290 | | } |
291 | | } |