/rust/registry/src/index.crates.io-1949cf8c6b5b557f/pxfm-0.1.27/src/bessel/i1.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::bessel::i0::bessel_rsqrt_hard; |
30 | | use crate::bessel::i0_exp; |
31 | | use crate::common::f_fmla; |
32 | | use crate::double_double::DoubleDouble; |
33 | | use crate::dyadic_float::{DyadicFloat128, DyadicSign}; |
34 | | use crate::exponents::rational128_exp; |
35 | | use crate::polyeval::{f_estrin_polyeval5, f_polyeval6}; |
36 | | |
37 | | /// Modified Bessel of the first kind of order 1 |
38 | | /// |
39 | | /// Max found ULP 0.5 |
40 | 0 | pub fn f_i1(x: f64) -> f64 { |
41 | 0 | let ux = x.to_bits().wrapping_shl(1); |
42 | | |
43 | 0 | if ux >= 0x7ffu64 << 53 || ux <= 0x7960000000000000u64 { |
44 | | // |x| <= f64::EPSILON, |x| == inf, x == NaN |
45 | 0 | if ux <= 0x760af31dc4611874u64 { |
46 | | // Power series of I1(x) ~ x/2 + O(x^3) |
47 | | // |x| <= 2.2204460492503131e-24 |
48 | 0 | return x * 0.5; |
49 | 0 | } |
50 | 0 | if ux <= 0x7960000000000000u64 { |
51 | | // |x| <= f64::EPSILON |
52 | | // Power series of I1(x) ~ x/2 + x^3/16 + O(x^4) |
53 | | const A0: f64 = 1. / 2.; |
54 | | const A1: f64 = 1. / 16.; |
55 | 0 | let r0 = f_fmla(x, x * A1, A0); |
56 | 0 | return r0 * x; |
57 | 0 | } |
58 | 0 | if x.is_infinite() { |
59 | 0 | return if x.is_sign_positive() { |
60 | 0 | f64::INFINITY |
61 | | } else { |
62 | 0 | f64::NEG_INFINITY |
63 | | }; |
64 | 0 | } |
65 | 0 | return x + f64::NAN; // x == NaN |
66 | 0 | } |
67 | | |
68 | 0 | let xb = x.to_bits() & 0x7fff_ffff_ffff_ffff; |
69 | | |
70 | 0 | if xb >= 0x40864fe69ff9fec8u64 { |
71 | | // |x| >= 713.9876098185423 |
72 | 0 | return if x.is_sign_negative() { |
73 | 0 | f64::NEG_INFINITY |
74 | | } else { |
75 | 0 | f64::INFINITY |
76 | | }; |
77 | 0 | } |
78 | | |
79 | | static SIGN: [f64; 2] = [1., -1.]; |
80 | | |
81 | 0 | let sign_scale = SIGN[x.is_sign_negative() as usize]; |
82 | | |
83 | 0 | if xb < 0x401f000000000000u64 { |
84 | | // |x| <= 7.75 |
85 | 0 | return f64::copysign(i1_0_to_7p75(f64::from_bits(xb)).to_f64(), sign_scale); |
86 | 0 | } |
87 | | |
88 | 0 | i1_asympt(f64::from_bits(xb), sign_scale) |
89 | 0 | } |
90 | | |
91 | | /** |
92 | | Computes |
93 | | I1(x) = x/2 * (1 + 1 * (x/2)^2 + (x/2)^4 * P((x/2)^2)) |
94 | | |
95 | | Polynomial generated by Wolfram Mathematica: |
96 | | ```text |
97 | | <<FunctionApproximations` |
98 | | ClearAll["Global`*"] |
99 | | f[x_]:=(BesselI[1,x]*2/x-1-1/2(x/2)^2)/(x/2)^4 |
100 | | g[z_]:=f[2 Sqrt[z]] |
101 | | {err,approx}=MiniMaxApproximation[g[z],{z,{0.000000001,7.5},9,9},WorkingPrecision->60] |
102 | | poly=Numerator[approx][[1]]; |
103 | | coeffs=CoefficientList[poly,z]; |
104 | | TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]] |
105 | | poly=Denominator[approx][[1]]; |
106 | | coeffs=CoefficientList[poly,z]; |
107 | | TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]] |
108 | | ``` |
109 | | **/ |
110 | | #[inline] |
111 | 0 | pub(crate) fn i1_0_to_7p75(x: f64) -> DoubleDouble { |
112 | 0 | let half_x = x * 0.5; // this is exact |
113 | 0 | let eval_x = DoubleDouble::from_exact_mult(half_x, half_x); |
114 | | |
115 | | const P: [(u64, u64); 5] = [ |
116 | | (0x3c55555555555555, 0x3fb5555555555555), |
117 | | (0x3c1253e1df138479, 0x3f7304597c4fbd4c), |
118 | | (0x3bcec398b7059ee9, 0x3f287b5b01f6b9c0), |
119 | | (0xbb7354e7c92c4f77, 0x3ed21de117470d10), |
120 | | (0xbb1d35ac0d7923cc, 0x3e717f3714dddc04), |
121 | | ]; |
122 | | |
123 | 0 | let ps_num = f_estrin_polyeval5( |
124 | 0 | eval_x.hi, |
125 | 0 | f64::from_bits(0x3e063684ca1944a4), |
126 | 0 | f64::from_bits(0x3d92ac4a0e48a9bb), |
127 | 0 | f64::from_bits(0x3d1425988b0b0aea), |
128 | 0 | f64::from_bits(0x3c899839e74ddefc), |
129 | 0 | f64::from_bits(0x3bed8747bcdd1e61), |
130 | | ); |
131 | | |
132 | 0 | let mut p_num = DoubleDouble::mul_f64_add(eval_x, ps_num, DoubleDouble::from_bit_pair(P[4])); |
133 | 0 | p_num = DoubleDouble::mul_add(eval_x, p_num, DoubleDouble::from_bit_pair(P[3])); |
134 | 0 | p_num = DoubleDouble::mul_add(eval_x, p_num, DoubleDouble::from_bit_pair(P[2])); |
135 | 0 | p_num = DoubleDouble::mul_add(eval_x, p_num, DoubleDouble::from_bit_pair(P[1])); |
136 | 0 | p_num = DoubleDouble::mul_add(eval_x, p_num, DoubleDouble::from_bit_pair(P[0])); |
137 | | |
138 | | const Q: [(u64, u64); 4] = [ |
139 | | (0x0000000000000000, 0x3ff0000000000000), |
140 | | (0xbc3e59afb81ac7ea, 0xbf9c4848e0661d70), |
141 | | (0x3bd62fa3dbc1a19c, 0x3f38a9eafcd7e674), |
142 | | (0x3b6f4688b9eab7d0, 0xbecbfdec51454533), |
143 | | ]; |
144 | | |
145 | 0 | let ps_den = f_polyeval6( |
146 | 0 | eval_x.hi, |
147 | 0 | f64::from_bits(0x3e56e7cde9266a32), |
148 | 0 | f64::from_bits(0xbddc316dff4a672f), |
149 | 0 | f64::from_bits(0x3d5a43aaee30ebb5), |
150 | 0 | f64::from_bits(0xbcd1fb023f4f1fa0), |
151 | 0 | f64::from_bits(0x3c4089ede324209f), |
152 | 0 | f64::from_bits(0xbb9f64f47ba69604), |
153 | | ); |
154 | | |
155 | 0 | let mut p_den = DoubleDouble::mul_f64_add(eval_x, ps_den, DoubleDouble::from_bit_pair(Q[3])); |
156 | 0 | p_den = DoubleDouble::mul_add(eval_x, p_den, DoubleDouble::from_bit_pair(Q[2])); |
157 | 0 | p_den = DoubleDouble::mul_add(eval_x, p_den, DoubleDouble::from_bit_pair(Q[1])); |
158 | 0 | p_den = DoubleDouble::mul_add_f64(eval_x, p_den, f64::from_bits(0x3ff0000000000000)); |
159 | | |
160 | 0 | let p = DoubleDouble::div(p_num, p_den); |
161 | | |
162 | 0 | let eval_sqr = DoubleDouble::quick_mult(eval_x, eval_x); |
163 | | |
164 | 0 | let mut z = DoubleDouble::mul_f64_add_f64(eval_x, 0.5, 1.); |
165 | 0 | z = DoubleDouble::mul_add(p, eval_sqr, z); |
166 | | |
167 | 0 | let x_over_05 = DoubleDouble::from_exact_mult(x, 0.5); |
168 | | |
169 | 0 | let r = DoubleDouble::quick_mult(z, x_over_05); |
170 | | |
171 | 0 | let err = f_fmla( |
172 | 0 | r.hi, |
173 | 0 | f64::from_bits(0x3c40000000000000), // 2^-59 |
174 | 0 | f64::from_bits(0x3be0000000000000), // 2^-65 |
175 | | ); |
176 | 0 | let ub = r.hi + (r.lo + err); |
177 | 0 | let lb = r.hi + (r.lo - err); |
178 | 0 | if ub == lb { |
179 | 0 | return r; |
180 | 0 | } |
181 | 0 | i1_0_to_7p5_hard(x) |
182 | 0 | } |
183 | | |
184 | | // Polynomial generated by Wolfram Mathematica: |
185 | | // I1(x) = x/2 * (1 + 1 * (x/2)^2 + (x/2)^4 * P((x/2)^2)) |
186 | | // <<FunctionApproximations` |
187 | | // ClearAll["Global`*"] |
188 | | // f[x_]:=(BesselI[1,x]*2/x-1-1/2(x/2)^2)/(x/2)^4 |
189 | | // g[z_]:=f[2 Sqrt[z]] |
190 | | // {err,approx}=MiniMaxApproximation[g[z],{z,{0.000000001,7.5},9,9},WorkingPrecision->60] |
191 | | // poly=Numerator[approx][[1]]; |
192 | | // coeffs=CoefficientList[poly,z]; |
193 | | // TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]] |
194 | | // poly=Denominator[approx][[1]]; |
195 | | // coeffs=CoefficientList[poly,z]; |
196 | | // TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]] |
197 | | #[cold] |
198 | | #[inline(never)] |
199 | 0 | pub(crate) fn i1_0_to_7p5_hard(x: f64) -> DoubleDouble { |
200 | | const ONE_OVER_4: f64 = 1. / 4.; |
201 | 0 | let eval_x = DoubleDouble::quick_mult_f64(DoubleDouble::from_exact_mult(x, x), ONE_OVER_4); |
202 | | |
203 | | const P: [(u64, u64); 10] = [ |
204 | | (0x3c55555555555555, 0x3fb5555555555555), |
205 | | (0x3c1253e1df138479, 0x3f7304597c4fbd4c), |
206 | | (0x3bcec398b7059ee9, 0x3f287b5b01f6b9c0), |
207 | | (0xbb7354e7c92c4f77, 0x3ed21de117470d10), |
208 | | (0xbb1d35ac0d7923cc, 0x3e717f3714dddc04), |
209 | | (0xba928dee24678e32, 0x3e063684ca1944a4), |
210 | | (0xba36aa59912fcbed, 0x3d92ac4a0e48a9bb), |
211 | | (0x39bad76f18b5ad37, 0x3d1425988b0b0aea), |
212 | | (0xb923a6bab6928df4, 0x3c899839e74ddefc), |
213 | | (0x3864356cdfa7b321, 0x3bed8747bcdd1e61), |
214 | | ]; |
215 | | |
216 | 0 | let mut p_num = DoubleDouble::mul_add( |
217 | 0 | eval_x, |
218 | 0 | DoubleDouble::from_bit_pair(P[9]), |
219 | 0 | DoubleDouble::from_bit_pair(P[8]), |
220 | | ); |
221 | 0 | p_num = DoubleDouble::mul_add(eval_x, p_num, DoubleDouble::from_bit_pair(P[7])); |
222 | 0 | p_num = DoubleDouble::mul_add(eval_x, p_num, DoubleDouble::from_bit_pair(P[6])); |
223 | 0 | p_num = DoubleDouble::mul_add(eval_x, p_num, DoubleDouble::from_bit_pair(P[5])); |
224 | 0 | p_num = DoubleDouble::mul_add(eval_x, p_num, DoubleDouble::from_bit_pair(P[4])); |
225 | 0 | p_num = DoubleDouble::mul_add(eval_x, p_num, DoubleDouble::from_bit_pair(P[3])); |
226 | 0 | p_num = DoubleDouble::mul_add(eval_x, p_num, DoubleDouble::from_bit_pair(P[2])); |
227 | 0 | p_num = DoubleDouble::mul_add(eval_x, p_num, DoubleDouble::from_bit_pair(P[1])); |
228 | 0 | p_num = DoubleDouble::mul_add(eval_x, p_num, DoubleDouble::from_bit_pair(P[0])); |
229 | | |
230 | | const Q: [(u64, u64); 10] = [ |
231 | | (0x0000000000000000, 0x3ff0000000000000), |
232 | | (0xbc3e59afb81ac7ea, 0xbf9c4848e0661d70), |
233 | | (0x3bd62fa3dbc1a19c, 0x3f38a9eafcd7e674), |
234 | | (0x3b6f4688b9eab7d0, 0xbecbfdec51454533), |
235 | | (0x3af0fb4a17103ef8, 0x3e56e7cde9266a32), |
236 | | (0xba71755779c6d4bd, 0xbddc316dff4a672f), |
237 | | (0x39cf8ed8d449e2c6, 0x3d5a43aaee30ebb5), |
238 | | (0x39704e900a373874, 0xbcd1fb023f4f1fa0), |
239 | | (0xb8e33e87e4bffbb1, 0x3c4089ede324209f), |
240 | | (0x380fb09b3fd49d5c, 0xbb9f64f47ba69604), |
241 | | ]; |
242 | | |
243 | 0 | let mut p_den = DoubleDouble::mul_add( |
244 | 0 | eval_x, |
245 | 0 | DoubleDouble::from_bit_pair(Q[9]), |
246 | 0 | DoubleDouble::from_bit_pair(Q[8]), |
247 | | ); |
248 | 0 | p_den = DoubleDouble::mul_add(eval_x, p_den, DoubleDouble::from_bit_pair(Q[7])); |
249 | 0 | p_den = DoubleDouble::mul_add(eval_x, p_den, DoubleDouble::from_bit_pair(Q[6])); |
250 | 0 | p_den = DoubleDouble::mul_add(eval_x, p_den, DoubleDouble::from_bit_pair(Q[5])); |
251 | 0 | p_den = DoubleDouble::mul_add(eval_x, p_den, DoubleDouble::from_bit_pair(Q[4])); |
252 | 0 | p_den = DoubleDouble::mul_add(eval_x, p_den, DoubleDouble::from_bit_pair(Q[3])); |
253 | 0 | p_den = DoubleDouble::mul_add(eval_x, p_den, DoubleDouble::from_bit_pair(Q[2])); |
254 | 0 | p_den = DoubleDouble::mul_add(eval_x, p_den, DoubleDouble::from_bit_pair(Q[1])); |
255 | 0 | p_den = DoubleDouble::mul_add(eval_x, p_den, DoubleDouble::from_bit_pair(Q[0])); |
256 | | |
257 | 0 | let p = DoubleDouble::div(p_num, p_den); |
258 | | |
259 | 0 | let eval_sqr = DoubleDouble::quick_mult(eval_x, eval_x); |
260 | | |
261 | 0 | let mut z = DoubleDouble::mul_f64_add_f64(eval_x, 0.5, 1.); |
262 | 0 | z = DoubleDouble::mul_add(p, eval_sqr, z); |
263 | | |
264 | 0 | let x_over_05 = DoubleDouble::from_exact_mult(x, 0.5); |
265 | | |
266 | 0 | DoubleDouble::quick_mult(z, x_over_05) |
267 | 0 | } |
268 | | |
269 | | /** |
270 | | Asymptotic expansion for I1. |
271 | | |
272 | | Computes: |
273 | | sqrt(x) * exp(-x) * I1(x) = Pn(1/x)/Qn(1/x) |
274 | | hence: |
275 | | I1(x) = Pn(1/x)/Qm(1/x)*exp(x)/sqrt(x) |
276 | | |
277 | | Generated by Wolfram Mathematica: |
278 | | ```text |
279 | | <<FunctionApproximations` |
280 | | ClearAll["Global`*"] |
281 | | f[x_]:=Sqrt[x] Exp[-x] BesselI[1,x] |
282 | | g[z_]:=f[1/z] |
283 | | {err,approx}=MiniMaxApproximation[g[z],{z,{1/713.98,1/7.75},11,11},WorkingPrecision->120] |
284 | | poly=Numerator[approx][[1]]; |
285 | | coeffs=CoefficientList[poly,z]; |
286 | | TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]] |
287 | | poly=Denominator[approx][[1]]; |
288 | | coeffs=CoefficientList[poly,z]; |
289 | | TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]] |
290 | | ``` |
291 | | **/ |
292 | | #[inline] |
293 | 0 | fn i1_asympt(x: f64, sign_scale: f64) -> f64 { |
294 | 0 | let dx = x; |
295 | 0 | let recip = DoubleDouble::from_quick_recip(x); |
296 | | const P: [(u64, u64); 12] = [ |
297 | | (0xbc73a823f28a2f5e, 0x3fd9884533d43651), |
298 | | (0x3cc0d5bb78e674b3, 0xc0354325c8029263), |
299 | | (0x3d20e1155aaaa283, 0x4080c09b027c46a4), |
300 | | (0xbd5b90dcf81b99c1, 0xc0bfc1311090c839), |
301 | | (0xbd98f2fda9e8fa1b, 0x40f3bb81bb190ae2), |
302 | | (0xbdcec960752b60da, 0xc1207c0bbbc31cd9), |
303 | | (0x3dd3c9a299c9c41f, 0x414253e25c4584af), |
304 | | (0xbde82e7b9a3e1acc, 0xc159a656aece377c), |
305 | | (0x3e0d3d30d701a8ab, 0x416398df24c74ef2), |
306 | | (0xbdf57b85ab7006e2, 0xc151fd119be1702b), |
307 | | (0x3dd760928f4515fd, 0xc1508327e42639bc), |
308 | | (0x3dc09e71bc648589, 0x4143e4933afa621c), |
309 | | ]; |
310 | | |
311 | 0 | let x2 = DoubleDouble::quick_mult(recip, recip); |
312 | 0 | let x4 = DoubleDouble::quick_mult(x2, x2); |
313 | 0 | let x8 = DoubleDouble::quick_mult(x4, x4); |
314 | | |
315 | 0 | let e0 = DoubleDouble::mul_add( |
316 | 0 | recip, |
317 | 0 | DoubleDouble::from_bit_pair(P[1]), |
318 | 0 | DoubleDouble::from_bit_pair(P[0]), |
319 | | ); |
320 | 0 | let e1 = DoubleDouble::mul_add( |
321 | 0 | recip, |
322 | 0 | DoubleDouble::from_bit_pair(P[3]), |
323 | 0 | DoubleDouble::from_bit_pair(P[2]), |
324 | | ); |
325 | 0 | let e2 = DoubleDouble::mul_add( |
326 | 0 | recip, |
327 | 0 | DoubleDouble::from_bit_pair(P[5]), |
328 | 0 | DoubleDouble::from_bit_pair(P[4]), |
329 | | ); |
330 | 0 | let e3 = DoubleDouble::mul_add( |
331 | 0 | recip, |
332 | 0 | DoubleDouble::from_bit_pair(P[7]), |
333 | 0 | DoubleDouble::from_bit_pair(P[6]), |
334 | | ); |
335 | 0 | let e4 = DoubleDouble::mul_add( |
336 | 0 | recip, |
337 | 0 | DoubleDouble::from_bit_pair(P[9]), |
338 | 0 | DoubleDouble::from_bit_pair(P[8]), |
339 | | ); |
340 | 0 | let e5 = DoubleDouble::mul_add( |
341 | 0 | recip, |
342 | 0 | DoubleDouble::from_bit_pair(P[11]), |
343 | 0 | DoubleDouble::from_bit_pair(P[10]), |
344 | | ); |
345 | | |
346 | 0 | let f0 = DoubleDouble::mul_add(x2, e1, e0); |
347 | 0 | let f1 = DoubleDouble::mul_add(x2, e3, e2); |
348 | 0 | let f2 = DoubleDouble::mul_add(x2, e5, e4); |
349 | | |
350 | 0 | let g0 = DoubleDouble::mul_add(x4, f1, f0); |
351 | | |
352 | 0 | let p_num = DoubleDouble::mul_add(x8, f2, g0); |
353 | | |
354 | | const Q: [(u64, u64); 12] = [ |
355 | | (0x0000000000000000, 0x3ff0000000000000), |
356 | | (0xbcb334d5a476d9ad, 0xc04a75f94c1a0c1a), |
357 | | (0xbd324d58ed98bfae, 0x4094b00e60301c42), |
358 | | (0x3d7c8725666c4360, 0xc0d36b9d28d45928), |
359 | | (0x3d7f8457c2945822, 0x4107d6c398a174ed), |
360 | | (0x3dbc655ea216594b, 0xc1339393e6776e38), |
361 | | (0xbdebb5dffef78272, 0x415537198d23f6a1), |
362 | | (0xbdb577f8abad883e, 0xc16c6c399dcd6949), |
363 | | (0x3e14261c5362f109, 0x4173c02446576949), |
364 | | (0x3dc382ededad42c5, 0xc1547dff5770f4ec), |
365 | | (0xbe05c0f74d4c7956, 0xc165c88046952562), |
366 | | (0xbdbf9145927aa2c7, 0x414395e46bc45d5b), |
367 | | ]; |
368 | | |
369 | 0 | let e0 = DoubleDouble::mul_add_f64( |
370 | 0 | recip, |
371 | 0 | DoubleDouble::from_bit_pair(Q[1]), |
372 | 0 | f64::from_bits(0x3ff0000000000000), |
373 | | ); |
374 | 0 | let e1 = DoubleDouble::mul_add( |
375 | 0 | recip, |
376 | 0 | DoubleDouble::from_bit_pair(Q[3]), |
377 | 0 | DoubleDouble::from_bit_pair(Q[2]), |
378 | | ); |
379 | 0 | let e2 = DoubleDouble::mul_add( |
380 | 0 | recip, |
381 | 0 | DoubleDouble::from_bit_pair(Q[5]), |
382 | 0 | DoubleDouble::from_bit_pair(Q[4]), |
383 | | ); |
384 | 0 | let e3 = DoubleDouble::mul_add( |
385 | 0 | recip, |
386 | 0 | DoubleDouble::from_bit_pair(Q[7]), |
387 | 0 | DoubleDouble::from_bit_pair(Q[6]), |
388 | | ); |
389 | 0 | let e4 = DoubleDouble::mul_add( |
390 | 0 | recip, |
391 | 0 | DoubleDouble::from_bit_pair(Q[9]), |
392 | 0 | DoubleDouble::from_bit_pair(Q[8]), |
393 | | ); |
394 | 0 | let e5 = DoubleDouble::mul_add( |
395 | 0 | recip, |
396 | 0 | DoubleDouble::from_bit_pair(Q[11]), |
397 | 0 | DoubleDouble::from_bit_pair(Q[10]), |
398 | | ); |
399 | | |
400 | 0 | let f0 = DoubleDouble::mul_add(x2, e1, e0); |
401 | 0 | let f1 = DoubleDouble::mul_add(x2, e3, e2); |
402 | 0 | let f2 = DoubleDouble::mul_add(x2, e5, e4); |
403 | | |
404 | 0 | let g0 = DoubleDouble::mul_add(x4, f1, f0); |
405 | | |
406 | 0 | let p_den = DoubleDouble::mul_add(x8, f2, g0); |
407 | | |
408 | 0 | let z = DoubleDouble::div(p_num, p_den); |
409 | | |
410 | 0 | let e = i0_exp(dx * 0.5); |
411 | 0 | let r_sqrt = DoubleDouble::from_rsqrt_fast(dx); |
412 | | |
413 | 0 | let r = DoubleDouble::quick_mult(z * r_sqrt * e, e); |
414 | | |
415 | 0 | let err = f_fmla( |
416 | 0 | r.hi, |
417 | 0 | f64::from_bits(0x3c40000000000000), // 2^-59 |
418 | 0 | f64::from_bits(0x3ba0000000000000), // 2^-69 |
419 | | ); |
420 | 0 | let up = r.hi + (r.lo + err); |
421 | 0 | let lb = r.hi + (r.lo - err); |
422 | 0 | if up == lb { |
423 | 0 | return f64::copysign(r.to_f64(), sign_scale); |
424 | 0 | } |
425 | 0 | i1_asympt_hard(x, sign_scale) |
426 | 0 | } |
427 | | |
428 | | /** |
429 | | Asymptotic expansion for I1. |
430 | | |
431 | | Computes: |
432 | | sqrt(x) * exp(-x) * I1(x) = Pn(1/x)/Qn(1/x) |
433 | | hence: |
434 | | I1(x) = Pn(1/x)/Qm(1/x)*exp(x)/sqrt(x) |
435 | | |
436 | | Generated by Wolfram Mathematica: |
437 | | ```text |
438 | | <<FunctionApproximations` |
439 | | ClearAll["Global`*"] |
440 | | f[x_]:=Sqrt[x] Exp[-x] BesselI[1,x] |
441 | | g[z_]:=f[1/z] |
442 | | {err,approx}=MiniMaxApproximation[g[z],{z,{1/713.98,1/7.75},15,15},WorkingPrecision->120] |
443 | | poly=Numerator[approx][[1]]; |
444 | | coeffs=CoefficientList[poly,z]; |
445 | | TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]] |
446 | | poly=Denominator[approx][[1]]; |
447 | | coeffs=CoefficientList[poly,z]; |
448 | | TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]] |
449 | | ``` |
450 | | **/ |
451 | | #[cold] |
452 | | #[inline(never)] |
453 | 0 | fn i1_asympt_hard(x: f64, sign_scale: f64) -> f64 { |
454 | | static P: [DyadicFloat128; 16] = [ |
455 | | DyadicFloat128 { |
456 | | sign: DyadicSign::Pos, |
457 | | exponent: -129, |
458 | | mantissa: 0xcc42299e_a1b28468_bea7da47_28f13acc_u128, |
459 | | }, |
460 | | DyadicFloat128 { |
461 | | sign: DyadicSign::Neg, |
462 | | exponent: -124, |
463 | | mantissa: 0xda979406_3df6e66f_cf31c3f5_f194b48c_u128, |
464 | | }, |
465 | | DyadicFloat128 { |
466 | | sign: DyadicSign::Neg, |
467 | | exponent: -120, |
468 | | mantissa: 0xd60b7b96_c958929b_cabe1d8c_3d874767_u128, |
469 | | }, |
470 | | DyadicFloat128 { |
471 | | sign: DyadicSign::Pos, |
472 | | exponent: -113, |
473 | | mantissa: 0xd27aad9a_8fb38d56_46ab4510_8479306e_u128, |
474 | | }, |
475 | | DyadicFloat128 { |
476 | | sign: DyadicSign::Neg, |
477 | | exponent: -108, |
478 | | mantissa: 0xe0167305_c451bd1f_d2f17b68_5c62e2ff_u128, |
479 | | }, |
480 | | DyadicFloat128 { |
481 | | sign: DyadicSign::Pos, |
482 | | exponent: -103, |
483 | | mantissa: 0x8f6d238f_c80d8e4a_08c130f6_24e1c925_u128, |
484 | | }, |
485 | | DyadicFloat128 { |
486 | | sign: DyadicSign::Neg, |
487 | | exponent: -100, |
488 | | mantissa: 0xfe32280f_2ea99024_d9924472_92d7ac8f_u128, |
489 | | }, |
490 | | DyadicFloat128 { |
491 | | sign: DyadicSign::Pos, |
492 | | exponent: -96, |
493 | | mantissa: 0xa48815ac_d265609f_da4ace94_811390b2_u128, |
494 | | }, |
495 | | DyadicFloat128 { |
496 | | sign: DyadicSign::Neg, |
497 | | exponent: -93, |
498 | | mantissa: 0x9ededfe5_833b4cc1_731efd5c_f8729c6c_u128, |
499 | | }, |
500 | | DyadicFloat128 { |
501 | | sign: DyadicSign::Pos, |
502 | | exponent: -91, |
503 | | mantissa: 0xe5b43203_2784ae6a_f7458556_0a8308ea_u128, |
504 | | }, |
505 | | DyadicFloat128 { |
506 | | sign: DyadicSign::Neg, |
507 | | exponent: -89, |
508 | | mantissa: 0xf5df279a_3fb4ef60_8d10adee_7dd2f47b_u128, |
509 | | }, |
510 | | DyadicFloat128 { |
511 | | sign: DyadicSign::Pos, |
512 | | exponent: -87, |
513 | | mantissa: 0xbdb59963_7a757ed1_87280e0e_7f93ca2b_u128, |
514 | | }, |
515 | | DyadicFloat128 { |
516 | | sign: DyadicSign::Neg, |
517 | | exponent: -86, |
518 | | mantissa: 0xc87fdea5_53177ca8_c91de5fb_3f8f78d3_u128, |
519 | | }, |
520 | | DyadicFloat128 { |
521 | | sign: DyadicSign::Pos, |
522 | | exponent: -85, |
523 | | mantissa: 0x846d16a7_4663ef5d_ad42d599_5bc726b8_u128, |
524 | | }, |
525 | | DyadicFloat128 { |
526 | | sign: DyadicSign::Neg, |
527 | | exponent: -86, |
528 | | mantissa: 0xb3ed2055_74262d95_389f33e4_2ac3774a_u128, |
529 | | }, |
530 | | DyadicFloat128 { |
531 | | sign: DyadicSign::Pos, |
532 | | exponent: -88, |
533 | | mantissa: 0xa511aa32_c18c34e4_3d029a90_a71b7a55_u128, |
534 | | }, |
535 | | ]; |
536 | | static Q: [DyadicFloat128; 16] = [ |
537 | | DyadicFloat128 { |
538 | | sign: DyadicSign::Pos, |
539 | | exponent: -127, |
540 | | mantissa: 0x80000000_00000000_00000000_00000000_u128, |
541 | | }, |
542 | | DyadicFloat128 { |
543 | | sign: DyadicSign::Neg, |
544 | | exponent: -122, |
545 | | mantissa: 0x877b771a_ad8f5fd3_5aacf5f9_f04ee9de_u128, |
546 | | }, |
547 | | DyadicFloat128 { |
548 | | sign: DyadicSign::Neg, |
549 | | exponent: -118, |
550 | | mantissa: 0x89475ecd_9c84361e_800c8a3a_c8af23bf_u128, |
551 | | }, |
552 | | DyadicFloat128 { |
553 | | sign: DyadicSign::Pos, |
554 | | exponent: -111, |
555 | | mantissa: 0x837d1196_cf2723f1_23b54da8_225efe05_u128, |
556 | | }, |
557 | | DyadicFloat128 { |
558 | | sign: DyadicSign::Neg, |
559 | | exponent: -106, |
560 | | mantissa: 0x8ae3aecb_15355751_a9ee12e5_a4dd9dde_u128, |
561 | | }, |
562 | | DyadicFloat128 { |
563 | | sign: DyadicSign::Pos, |
564 | | exponent: -102, |
565 | | mantissa: 0xb0886afa_bc13f996_ab45d252_75c8f587_u128, |
566 | | }, |
567 | | DyadicFloat128 { |
568 | | sign: DyadicSign::Neg, |
569 | | exponent: -98, |
570 | | mantissa: 0x9b37d7cd_b114b86b_7d14a389_26599aa1_u128, |
571 | | }, |
572 | | DyadicFloat128 { |
573 | | sign: DyadicSign::Pos, |
574 | | exponent: -95, |
575 | | mantissa: 0xc716bf54_09d5dd9f_bc16679b_93aaeca4_u128, |
576 | | }, |
577 | | DyadicFloat128 { |
578 | | sign: DyadicSign::Neg, |
579 | | exponent: -92, |
580 | | mantissa: 0xbe0cd82e_c8af8371_ab028ed9_c7902dd2_u128, |
581 | | }, |
582 | | DyadicFloat128 { |
583 | | sign: DyadicSign::Pos, |
584 | | exponent: -89, |
585 | | mantissa: 0x875f8d91_8ef5d434_a39d00f9_2aed3d2a_u128, |
586 | | }, |
587 | | DyadicFloat128 { |
588 | | sign: DyadicSign::Neg, |
589 | | exponent: -87, |
590 | | mantissa: 0x8e030781_5aa4ce7f_70156b82_8b216b7c_u128, |
591 | | }, |
592 | | DyadicFloat128 { |
593 | | sign: DyadicSign::Pos, |
594 | | exponent: -86, |
595 | | mantissa: 0xd4dd2687_92646fbd_5ea2d422_da64fc0b_u128, |
596 | | }, |
597 | | DyadicFloat128 { |
598 | | sign: DyadicSign::Neg, |
599 | | exponent: -85, |
600 | | mantissa: 0xd6d72ab3_64b4a827_0499af0f_13a51a80_u128, |
601 | | }, |
602 | | DyadicFloat128 { |
603 | | sign: DyadicSign::Pos, |
604 | | exponent: -84, |
605 | | mantissa: 0x828f4e8b_728747a9_2cebe54a_810e2681_u128, |
606 | | }, |
607 | | DyadicFloat128 { |
608 | | sign: DyadicSign::Neg, |
609 | | exponent: -85, |
610 | | mantissa: 0x91570096_36a3fcfb_6b936d44_68dda1be_u128, |
611 | | }, |
612 | | DyadicFloat128 { |
613 | | sign: DyadicSign::Pos, |
614 | | exponent: -89, |
615 | | mantissa: 0xf082ad00_86024ed4_dd31613b_ec41e3f8_u128, |
616 | | }, |
617 | | ]; |
618 | | |
619 | 0 | let recip = DyadicFloat128::accurate_reciprocal(x); |
620 | | |
621 | 0 | let mut p_num = P[15]; |
622 | 0 | for i in (0..15).rev() { |
623 | 0 | p_num = recip * p_num + P[i]; |
624 | 0 | } |
625 | 0 | let mut p_den = Q[15]; |
626 | 0 | for i in (0..15).rev() { |
627 | 0 | p_den = recip * p_den + Q[i]; |
628 | 0 | } |
629 | 0 | let z = p_num * p_den.reciprocal(); |
630 | 0 | let r_sqrt = bessel_rsqrt_hard(x, recip); |
631 | 0 | let f_exp = rational128_exp(x); |
632 | 0 | (z * r_sqrt * f_exp).fast_as_f64() * sign_scale |
633 | 0 | } |
634 | | |
635 | | #[cfg(test)] |
636 | | mod tests { |
637 | | use super::*; |
638 | | #[test] |
639 | | fn test_fi1() { |
640 | | assert_eq!( |
641 | | f_i1(0.0000000000000000000000000000000006423424234121), |
642 | | 3.2117121170605e-34 |
643 | | ); |
644 | | assert_eq!(f_i1(7.750000000757874), 315.8524811496668); |
645 | | assert_eq!(f_i1(7.482812501363189), 245.58002285881892); |
646 | | assert_eq!(f_i1(-7.750000000757874), -315.8524811496668); |
647 | | assert_eq!(f_i1(-7.482812501363189), -245.58002285881892); |
648 | | assert!(f_i1(f64::NAN).is_nan()); |
649 | | assert_eq!(f_i1(f64::INFINITY), f64::INFINITY); |
650 | | assert_eq!(f_i1(f64::NEG_INFINITY), f64::NEG_INFINITY); |
651 | | assert_eq!(f_i1(0.01), 0.005000062500260418); |
652 | | assert_eq!(f_i1(-0.01), -0.005000062500260418); |
653 | | assert_eq!(f_i1(-9.01), -1040.752038018038); |
654 | | assert_eq!(f_i1(9.01), 1040.752038018038); |
655 | | } |
656 | | } |