/rust/registry/src/index.crates.io-1949cf8c6b5b557f/pxfm-0.1.27/src/hyperbolic/sinhf.rs
Line | Count | Source |
1 | | /* |
2 | | * // Copyright (c) Radzivon Bartoshyk 6/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::{f_fmla, f_fmlaf}; |
30 | | use crate::rounding::CpuRoundTiesEven; |
31 | | |
32 | | static TB: [u64; 32] = [ |
33 | | 0x3fe0000000000000, |
34 | | 0x3fe059b0d3158574, |
35 | | 0x3fe0b5586cf9890f, |
36 | | 0x3fe11301d0125b51, |
37 | | 0x3fe172b83c7d517b, |
38 | | 0x3fe1d4873168b9aa, |
39 | | 0x3fe2387a6e756238, |
40 | | 0x3fe29e9df51fdee1, |
41 | | 0x3fe306fe0a31b715, |
42 | | 0x3fe371a7373aa9cb, |
43 | | 0x3fe3dea64c123422, |
44 | | 0x3fe44e086061892d, |
45 | | 0x3fe4bfdad5362a27, |
46 | | 0x3fe5342b569d4f82, |
47 | | 0x3fe5ab07dd485429, |
48 | | 0x3fe6247eb03a5585, |
49 | | 0x3fe6a09e667f3bcd, |
50 | | 0x3fe71f75e8ec5f74, |
51 | | 0x3fe7a11473eb0187, |
52 | | 0x3fe82589994cce13, |
53 | | 0x3fe8ace5422aa0db, |
54 | | 0x3fe93737b0cdc5e5, |
55 | | 0x3fe9c49182a3f090, |
56 | | 0x3fea5503b23e255d, |
57 | | 0x3feae89f995ad3ad, |
58 | | 0x3feb7f76f2fb5e47, |
59 | | 0x3fec199bdd85529c, |
60 | | 0x3fecb720dcef9069, |
61 | | 0x3fed5818dcfba487, |
62 | | 0x3fedfc97337b9b5f, |
63 | | 0x3feea4afa2a490da, |
64 | | 0x3fef50765b6e4540, |
65 | | ]; |
66 | | |
67 | | #[cold] |
68 | 0 | fn sinhf_accurate(z: f64, ia: f64, sp: u64, sm: u64) -> f32 { |
69 | | const CH: [u64; 7] = [ |
70 | | 0x3ff0000000000000, |
71 | | 0x3f962e42fefa39ef, |
72 | | 0x3f2ebfbdff82c58f, |
73 | | 0x3ebc6b08d702e0ed, |
74 | | 0x3e43b2ab6fb92e5e, |
75 | | 0x3dc5d886e6d54203, |
76 | | 0x3d4430976b8ce6ef, |
77 | | ]; |
78 | | const ILN2H: f64 = f64::from_bits(0x4047154765000000); |
79 | | const ILN2L: f64 = f64::from_bits(0x3e55c17f0bbbe880); |
80 | 0 | let h = f_fmla(ILN2L, z, f_fmla(ILN2H, z, -ia)); |
81 | 0 | let h2 = h * h; |
82 | | |
83 | 0 | let q0 = f_fmla(h2, f64::from_bits(CH[6]), f64::from_bits(CH[4])); |
84 | 0 | let q1 = f_fmla(h2, f64::from_bits(CH[2]), f64::from_bits(CH[0])); |
85 | | |
86 | 0 | let te = f_fmla(h2 * h2, q0, q1); |
87 | | |
88 | 0 | let q2 = f_fmla(h2, f64::from_bits(CH[5]), f64::from_bits(CH[3])); |
89 | | |
90 | 0 | let to = f_fmla(h2, q2, f64::from_bits(CH[1])); |
91 | | |
92 | 0 | let b0 = f_fmla(h, to, te); |
93 | 0 | let b1 = f_fmla(-h, to, te); |
94 | | |
95 | 0 | f_fmla(f64::from_bits(sp), b0, -f64::from_bits(sm) * b1) as f32 |
96 | 0 | } |
97 | | |
98 | | /// Huperbolic sine function |
99 | | /// |
100 | | /// Max found ULP 0.4954563 |
101 | | #[inline] |
102 | 0 | pub fn f_sinhf(x: f32) -> f32 { |
103 | | const C: [u64; 4] = [ |
104 | | 0x3ff0000000000000, |
105 | | 0x3f962e42fef4c4e7, |
106 | | 0x3f2ebfd1b232f475, |
107 | | 0x3ebc6b19384ecd93, |
108 | | ]; |
109 | | |
110 | | const ST: [(u32, u32, u32); 1] = [(0x74250bfeu32, 0x3a1285ff, 0x2d800000)]; |
111 | | const ILN2: f64 = f64::from_bits(0x40471547652b82fe); |
112 | 0 | let t = x.to_bits(); |
113 | 0 | let z: f64 = x as f64; |
114 | 0 | let ux = t.wrapping_shl(1); |
115 | 0 | if ux > 0x8565a9f8u32 { |
116 | | // |x| > 89.416 |
117 | 0 | let sgn = f32::copysign(2.0, x); |
118 | 0 | if ux >= 0xff000000u32 { |
119 | 0 | if ux.wrapping_shl(8) != 0 { |
120 | 0 | return x + x; |
121 | 0 | } // nan |
122 | 0 | return sgn * f32::INFINITY; // +-inf |
123 | 0 | } |
124 | 0 | let r = sgn * f64::from_bits(0x47efffffe0000000) as f32; |
125 | | |
126 | 0 | return r; |
127 | 0 | } |
128 | 0 | if ux < 0x7c000000u32 { |
129 | | // |x| < 0.125 |
130 | 0 | if ux <= 0x74250bfeu32 { |
131 | | // |x| <= 0.000558942 |
132 | 0 | if ux < 0x66000000u32 { |
133 | | // |x| < 5.96046e-08 |
134 | | #[cfg(any( |
135 | | all( |
136 | | any(target_arch = "x86", target_arch = "x86_64"), |
137 | | target_feature = "fma" |
138 | | ), |
139 | | target_arch = "aarch64" |
140 | | ))] |
141 | | { |
142 | | use crate::common::f_fmlaf; |
143 | | return f_fmlaf(x, x.abs(), x); |
144 | | } |
145 | | #[cfg(not(any( |
146 | | all( |
147 | | any(target_arch = "x86", target_arch = "x86_64"), |
148 | | target_feature = "fma" |
149 | | ), |
150 | | target_arch = "aarch64" |
151 | | )))] |
152 | | { |
153 | 0 | let dx = x as f64; |
154 | 0 | return f_fmla(dx, dx.abs(), dx) as f32; |
155 | | } |
156 | 0 | } |
157 | 0 | if ST[0].0 == ux { |
158 | 0 | let sgn = f32::copysign(1.0, x); |
159 | 0 | return f_fmlaf(sgn, f32::from_bits(ST[0].1), sgn * f32::from_bits(ST[0].2)); |
160 | 0 | } |
161 | | #[cfg(any( |
162 | | all( |
163 | | any(target_arch = "x86", target_arch = "x86_64"), |
164 | | target_feature = "fma" |
165 | | ), |
166 | | target_arch = "aarch64" |
167 | | ))] |
168 | | { |
169 | | use crate::common::f_fmlaf; |
170 | | return f_fmlaf(x * f64::from_bits(0x3fc5555560000000) as f32, x * x, x); |
171 | | } |
172 | | #[cfg(not(any( |
173 | | all( |
174 | | any(target_arch = "x86", target_arch = "x86_64"), |
175 | | target_feature = "fma" |
176 | | ), |
177 | | target_arch = "aarch64" |
178 | | )))] |
179 | | { |
180 | 0 | let dx = x as f64; |
181 | 0 | return f_fmla(dx * f64::from_bits(0x3fc5555560000000), dx * dx, dx) as f32; |
182 | | } |
183 | 0 | } |
184 | | const CP: [u64; 4] = [ |
185 | | 0x3fc5555555555555, |
186 | | 0x3f811111111146e1, |
187 | | 0x3f2a01a00930dda6, |
188 | | 0x3ec71f92198aa6e9, |
189 | | ]; |
190 | 0 | let z2 = z * z; |
191 | 0 | let z4 = z2 * z2; |
192 | | |
193 | 0 | let w0 = f_fmla(z2, f64::from_bits(CP[3]), f64::from_bits(CP[2])); |
194 | 0 | let w1 = f_fmla(z2, f64::from_bits(CP[1]), f64::from_bits(CP[0])); |
195 | 0 | let w2 = f_fmla(z4, w0, w1); |
196 | | |
197 | 0 | return f_fmla(z2 * z, w2, z) as f32; |
198 | 0 | } |
199 | 0 | let a = ILN2 * z; |
200 | 0 | let ia = a.cpu_round_ties_even(); |
201 | 0 | let h = a - ia; |
202 | 0 | let h2 = h * h; |
203 | 0 | let ja = (ia + f64::from_bits(0x4338000000000000)).to_bits(); |
204 | 0 | let jp: i64 = ja as i64; |
205 | 0 | let jm = -jp; |
206 | 0 | let sp = TB[(jp & 31) as usize].wrapping_add(jp.wrapping_shr(5).wrapping_shl(52) as u64); |
207 | 0 | let sm = TB[(jm & 31) as usize].wrapping_add(jm.wrapping_shr(5).wrapping_shl(52) as u64); |
208 | 0 | let te = f_fmla(h2, f64::from_bits(C[2]), f64::from_bits(C[0])); |
209 | 0 | let to = f_fmla(h2, f64::from_bits(C[3]), f64::from_bits(C[1])); |
210 | 0 | let rp = f64::from_bits(sp) * f_fmla(h, to, te); |
211 | 0 | let rm = f64::from_bits(sm) * f_fmla(-h, to, te); |
212 | 0 | let r = rp - rm; |
213 | 0 | let ub = r; |
214 | 0 | let lb = r - f64::from_bits(0x3de4e406496685f4) * r; |
215 | | // Ziv's accuracy test |
216 | 0 | if ub != lb { |
217 | 0 | return sinhf_accurate(z, ia, sp, sm); |
218 | 0 | } |
219 | 0 | ub as f32 |
220 | 0 | } |
221 | | |
222 | | #[cfg(test)] |
223 | | mod tests { |
224 | | use super::*; |
225 | | |
226 | | #[test] |
227 | | fn test_sinhf() { |
228 | | assert_eq!(f_sinhf(-0.5), -0.5210953); |
229 | | assert_eq!(f_sinhf(0.5), 0.5210953); |
230 | | assert_eq!(f_sinhf(7.), 548.3161); |
231 | | } |
232 | | } |