/rust/registry/src/index.crates.io-1949cf8c6b5b557f/pxfm-0.1.27/src/bessel/k0.rs
Line | Count | Source |
1 | | /* |
2 | | * // Copyright (c) Radzivon Bartoshyk 8/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::double_double::DoubleDouble; |
32 | | use crate::dyadic_float::{DyadicFloat128, DyadicSign}; |
33 | | use crate::exponents::rational128_exp; |
34 | | use crate::logs::{fast_log_d_to_dd, log_dd}; |
35 | | use crate::polyeval::f_polyeval3; |
36 | | |
37 | | /// Modified Bessel of the second kind of order 0 |
38 | | /// |
39 | | /// Max ULP 0.5 |
40 | 0 | pub fn f_k0(x: f64) -> f64 { |
41 | 0 | let ix = x.to_bits(); |
42 | | |
43 | 0 | if ix >= 0x7ffu64 << 52 || ix == 0 { |
44 | | // |x| == NaN, x == inf, |x| == 0, x < 0 |
45 | 0 | if ix.wrapping_shl(1) == 0 { |
46 | | // |x| == 0 |
47 | 0 | return f64::INFINITY; |
48 | 0 | } |
49 | 0 | if x.is_infinite() { |
50 | 0 | return if x.is_sign_positive() { 0. } else { f64::NAN }; |
51 | 0 | } |
52 | 0 | return x + f64::NAN; // x == NaN |
53 | 0 | } |
54 | | |
55 | 0 | let xb = x.to_bits(); |
56 | | |
57 | 0 | if xb >= 0x40862e42fefa39f0u64 { |
58 | | // x >= 709.7827128933841 |
59 | 0 | return 0.; |
60 | 0 | } |
61 | | |
62 | 0 | if xb <= 0x3ff0000000000000 { |
63 | | // x <= 1 |
64 | 0 | return k0_small_dd(x).to_f64(); |
65 | 0 | } |
66 | | |
67 | 0 | k0_asympt(x) |
68 | 0 | } |
69 | | |
70 | | /** |
71 | | Computes I0 on interval [0; 1] |
72 | | as rational approximation I0 = 1 + (x/2)^2 * Pn((x/2)^2)/Qm((x/2)^2)) |
73 | | |
74 | | Generated by Wolfram Mathematica: |
75 | | ```python |
76 | | <<FunctionApproximations` |
77 | | ClearAll["Global`*"] |
78 | | f[x_]:=(BesselI[0,x]-1)/(x/2)^2 |
79 | | g[z_]:=f[2 Sqrt[z]] |
80 | | {err,approx}=MiniMaxApproximation[g[z],{z,{0.0000000000001,1},5,5},WorkingPrecision->60] |
81 | | poly=Numerator[approx][[1]]; |
82 | | coeffs=CoefficientList[poly,z]; |
83 | | TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]] |
84 | | poly=Denominator[approx][[1]]; |
85 | | coeffs=CoefficientList[poly,z]; |
86 | | TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]] |
87 | | ``` |
88 | | **/ |
89 | | #[inline] |
90 | 0 | fn i0_0_to_1_fast(x: f64) -> DoubleDouble { |
91 | 0 | let half_x = x * 0.5; // this is exact |
92 | 0 | let eval_x = DoubleDouble::from_exact_mult(half_x, half_x); |
93 | | |
94 | | const P: [(u64, u64); 3] = [ |
95 | | (0xbae20452afd5045b, 0x3ff0000000000000), |
96 | | (0xbc5b6ff3f140da20, 0x3fc93c83592c03de), |
97 | | (0x3c25b350e9128d49, 0x3f904f33ef2de455), |
98 | | ]; |
99 | | |
100 | 0 | let ps_num = f_polyeval3( |
101 | 0 | eval_x.hi, |
102 | 0 | f64::from_bits(0x3f433805a2fabaaa), |
103 | 0 | f64::from_bits(0x3ee5897e7f554966), |
104 | 0 | f64::from_bits(0x3e731401f0bb5de4), |
105 | | ); |
106 | 0 | let mut p_num = DoubleDouble::mul_f64_add(eval_x, ps_num, DoubleDouble::from_bit_pair(P[2])); |
107 | 0 | p_num = DoubleDouble::mul_add(eval_x, p_num, DoubleDouble::from_bit_pair(P[1])); |
108 | 0 | p_num = DoubleDouble::mul_add(eval_x, p_num, DoubleDouble::from_bit_pair(P[0])); |
109 | | |
110 | | const Q: [(u64, u64); 3] = [ |
111 | | (0x0000000000000000, 0x3ff0000000000000), |
112 | | (0x3c323fa63bef2b4e, 0xbfab0df29b4ff089), |
113 | | (0x3bfedbdf64ed3110, 0x3f564662064157d2), |
114 | | ]; |
115 | | |
116 | 0 | let ps_den = f_polyeval3( |
117 | 0 | eval_x.hi, |
118 | 0 | f64::from_bits(0xbef6bdbb484fd0a4), |
119 | 0 | f64::from_bits(0x3e8d6ced53309351), |
120 | 0 | f64::from_bits(0xbe13cff13854e945), |
121 | | ); |
122 | 0 | let mut p_den = DoubleDouble::mul_f64_add(eval_x, ps_den, DoubleDouble::from_bit_pair(Q[2])); |
123 | 0 | p_den = DoubleDouble::mul_add(eval_x, p_den, DoubleDouble::from_bit_pair(Q[1])); |
124 | 0 | p_den = DoubleDouble::mul_add(eval_x, p_den, DoubleDouble::from_bit_pair(Q[0])); |
125 | 0 | let p = DoubleDouble::div(p_num, p_den); |
126 | | |
127 | 0 | let z = DoubleDouble::quick_mult(p, eval_x); |
128 | | |
129 | 0 | DoubleDouble::full_add_f64(z, 1.) |
130 | 0 | } |
131 | | |
132 | | /** |
133 | | K0(x) + log(x) * I0(x) = P(x^2) |
134 | | hence |
135 | | K0(x) = P(x^2) - log(x)*I0(x) |
136 | | |
137 | | Generated in Wolfram Mathematica: |
138 | | ```text |
139 | | <<FunctionApproximations` |
140 | | ClearAll["Global`*"] |
141 | | f[x_]:=BesselK[0,x]+Log[x]BesselI[0,x] |
142 | | g[z_]:=f[Sqrt[z]] |
143 | | {err,approx}=MiniMaxApproximation[g[z],{z,{0.000000001,1},5,5},WorkingPrecision->60] |
144 | | poly=Numerator[approx][[1]]; |
145 | | coeffs=CoefficientList[poly,z]; |
146 | | TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]] |
147 | | poly=Denominator[approx][[1]]; |
148 | | coeffs=CoefficientList[poly,z]; |
149 | | TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]] |
150 | | ``` |
151 | | **/ |
152 | | #[inline] |
153 | 0 | pub(crate) fn k0_small_dd(x: f64) -> DoubleDouble { |
154 | 0 | let dx = DoubleDouble::from_exact_mult(x, x); |
155 | | const P: [(u64, u64); 6] = [ |
156 | | (0x3c1be095d044e896, 0x3fbdadb014541eb2), |
157 | | (0x3c7321baa1d0a2d9, 0x3fd1b9f19bc9019a), |
158 | | (0xbc33ce33a244e5bd, 0x3f94ec39f8744183), |
159 | | (0x3bd7008dfc623255, 0x3f3d85175b25727d), |
160 | | (0xbb4aa2a1c4905d30, 0x3ed007a860ef3235), |
161 | | (0xbae8daa77abd6f7f, 0x3e4839e32c19f31a), |
162 | | ]; |
163 | | |
164 | 0 | let ps_num = f_polyeval3( |
165 | 0 | dx.hi, |
166 | 0 | f64::from_bits(0x3f3d85175b25727d), |
167 | 0 | f64::from_bits(0x3ed007a860ef3235), |
168 | 0 | f64::from_bits(0x3e4839e32c19f31a), |
169 | | ); |
170 | | |
171 | 0 | let mut p_num = DoubleDouble::mul_f64_add(dx, ps_num, DoubleDouble::from_bit_pair(P[2])); |
172 | 0 | p_num = DoubleDouble::mul_add(dx, p_num, DoubleDouble::from_bit_pair(P[1])); |
173 | 0 | p_num = DoubleDouble::mul_add(dx, p_num, DoubleDouble::from_bit_pair(P[0])); |
174 | | |
175 | | const Q: [(u64, u64); 3] = [ |
176 | | (0x0000000000000000, 0x3ff0000000000000), |
177 | | (0xbc2a82a292acdc83, 0xbf91be3a25c968d6), |
178 | | (0xbb9d2c37183a6496, 0x3f23bac6961619d8), |
179 | | ]; |
180 | | |
181 | 0 | let ps_den = f_polyeval3( |
182 | 0 | dx.hi, |
183 | 0 | f64::from_bits(0xbeac315b81faa1bf), |
184 | 0 | f64::from_bits(0x3e2ab2d2fbae0863), |
185 | 0 | f64::from_bits(0xbd9be23550f83df7), |
186 | | ); |
187 | 0 | let mut p_den = DoubleDouble::mul_f64_add(dx, ps_den, DoubleDouble::from_bit_pair(Q[2])); |
188 | 0 | p_den = DoubleDouble::mul_add(dx, p_den, DoubleDouble::from_bit_pair(Q[1])); |
189 | 0 | p_den = DoubleDouble::mul_add_f64(dx, p_den, f64::from_bits(0x3ff0000000000000)); |
190 | | |
191 | 0 | let prod = DoubleDouble::div(p_num, p_den); |
192 | | |
193 | 0 | let vi_log = fast_log_d_to_dd(x); |
194 | 0 | let vi = i0_0_to_1_fast(x); |
195 | 0 | let r = DoubleDouble::mul_add(vi_log, -vi, prod); |
196 | 0 | let err = r.hi * f64::from_bits(0x3c00000000000000); // 2^-63 |
197 | 0 | let ub = r.hi + (r.lo + err); |
198 | 0 | let lb = r.hi + (r.lo - err); |
199 | 0 | if ub == lb { |
200 | 0 | return r; |
201 | 0 | } |
202 | | |
203 | 0 | k0_small_hard(x, vi) |
204 | 0 | } |
205 | | |
206 | | /** |
207 | | K0(x) + log(x) * I0(x) = P(x^2) |
208 | | hence |
209 | | K0(x) = P(x^2) - log(x)*I0(x) |
210 | | |
211 | | Generated in Wolfram Mathematica: |
212 | | ```text |
213 | | <<FunctionApproximations` |
214 | | ClearAll["Global`*"] |
215 | | f[x_]:=BesselK[0,x]+Log[x]BesselI[0,x] |
216 | | g[z_]:=f[Sqrt[z]] |
217 | | {err,approx}=MiniMaxApproximation[g[z],{z,{0.000000001,1},5,5},WorkingPrecision->60] |
218 | | poly=Numerator[approx][[1]]; |
219 | | coeffs=CoefficientList[poly,z]; |
220 | | TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]] |
221 | | poly=Denominator[approx][[1]]; |
222 | | coeffs=CoefficientList[poly,z]; |
223 | | TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]] |
224 | | ``` |
225 | | **/ |
226 | | #[cold] |
227 | | #[inline(never)] |
228 | 0 | fn k0_small_hard(x: f64, vi: DoubleDouble) -> DoubleDouble { |
229 | 0 | let dx = DoubleDouble::from_exact_mult(x, x); |
230 | | const P: [(u64, u64); 6] = [ |
231 | | (0x3c1be095d044e896, 0x3fbdadb014541eb2), |
232 | | (0x3c7321baa1d0a2d9, 0x3fd1b9f19bc9019a), |
233 | | (0xbc33ce33a244e5bd, 0x3f94ec39f8744183), |
234 | | (0x3bd7008dfc623255, 0x3f3d85175b25727d), |
235 | | (0xbb4aa2a1c4905d30, 0x3ed007a860ef3235), |
236 | | (0xbae8daa77abd6f7f, 0x3e4839e32c19f31a), |
237 | | ]; |
238 | | |
239 | 0 | let mut p_num = DoubleDouble::mul_add( |
240 | 0 | dx, |
241 | 0 | DoubleDouble::from_bit_pair(P[5]), |
242 | 0 | DoubleDouble::from_bit_pair(P[4]), |
243 | | ); |
244 | 0 | p_num = DoubleDouble::mul_add(dx, p_num, DoubleDouble::from_bit_pair(P[3])); |
245 | 0 | p_num = DoubleDouble::mul_add(dx, p_num, DoubleDouble::from_bit_pair(P[2])); |
246 | 0 | p_num = DoubleDouble::mul_add(dx, p_num, DoubleDouble::from_bit_pair(P[1])); |
247 | 0 | p_num = DoubleDouble::mul_add(dx, p_num, DoubleDouble::from_bit_pair(P[0])); |
248 | | |
249 | | const Q: [(u64, u64); 6] = [ |
250 | | (0x0000000000000000, 0x3ff0000000000000), |
251 | | (0xbc2a82a292acdc83, 0xbf91be3a25c968d6), |
252 | | (0xbb9d2c37183a6496, 0x3f23bac6961619d8), |
253 | | (0xbb32032e14c6c2b2, 0xbeac315b81faa1bf), |
254 | | (0x3aa1a1dc04bfba96, 0x3e2ab2d2fbae0863), |
255 | | (0x3a3e0f678099fcff, 0xbd9be23550f83df7), |
256 | | ]; |
257 | | |
258 | 0 | let mut p_den = DoubleDouble::mul_add( |
259 | 0 | dx, |
260 | 0 | DoubleDouble::from_bit_pair(Q[5]), |
261 | 0 | DoubleDouble::from_bit_pair(Q[4]), |
262 | | ); |
263 | 0 | p_den = DoubleDouble::mul_add(dx, p_den, DoubleDouble::from_bit_pair(Q[3])); |
264 | 0 | p_den = DoubleDouble::mul_add(dx, p_den, DoubleDouble::from_bit_pair(Q[2])); |
265 | 0 | p_den = DoubleDouble::mul_add(dx, p_den, DoubleDouble::from_bit_pair(Q[1])); |
266 | 0 | p_den = DoubleDouble::mul_add_f64(dx, p_den, f64::from_bits(0x3ff0000000000000)); |
267 | | |
268 | 0 | let prod = DoubleDouble::div(p_num, p_den); |
269 | | |
270 | 0 | let v_log = log_dd(x); |
271 | | |
272 | 0 | DoubleDouble::mul_add(v_log, -vi, prod) |
273 | 0 | } |
274 | | |
275 | | /** |
276 | | Generated in Wolfram |
277 | | |
278 | | Computes sqrt(x)*exp(x)*K0(x)=Pn(1/x)/Qm(1/x) |
279 | | hence |
280 | | K0(x) = Pn(1/x)/Qm(1/x) / (sqrt(x) * exp(x)) |
281 | | |
282 | | ```text |
283 | | <<FunctionApproximations` |
284 | | ClearAll["Global`*"] |
285 | | f[x_]:=Sqrt[x] Exp[x] BesselK[0,x] |
286 | | g[z_]:=f[1/z] |
287 | | {err, approx}=MiniMaxApproximation[g[z],{z,{0.0000000000001,1},11,11},WorkingPrecision->60] |
288 | | poly=Numerator[approx][[1]]; |
289 | | coeffs=CoefficientList[poly,z]; |
290 | | TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]] |
291 | | poly=Denominator[approx][[1]]; |
292 | | coeffs=CoefficientList[poly,z]; |
293 | | TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]] |
294 | | ``` |
295 | | **/ |
296 | | #[inline] |
297 | 0 | fn k0_asympt(x: f64) -> f64 { |
298 | 0 | let recip = DoubleDouble::from_quick_recip(x); |
299 | 0 | let e = i0_exp(x * 0.5); |
300 | 0 | let r_sqrt = DoubleDouble::from_sqrt(x); |
301 | | |
302 | | const P: [(u64, u64); 12] = [ |
303 | | (0xbc9a6a11d237114e, 0x3ff40d931ff62706), |
304 | | (0x3cdd614ddf4929e5, 0x4040645168c3e483), |
305 | | (0xbd1ecf9ea0af6ab2, 0x40757419a703a2ab), |
306 | | (0xbd3da3551fb27770, 0x409d4e65365522a2), |
307 | | (0xbd564d58855d1a46, 0x40b6dd32f5a199d9), |
308 | | (0xbd6cf055ca963a8e, 0x40c4fd2368f19618), |
309 | | (0x3d4b6cdfbdc058df, 0x40c68faa11ebcd59), |
310 | | (0x3d5b4ce4665bfa46, 0x40bb6fbe08e0a8ea), |
311 | | (0xbd4316909063be15, 0x40a1953103a5be31), |
312 | | (0x3d12f3f8edf41af0, 0x4074d2cb001e175c), |
313 | | (0xbcd7bba36540264f, 0x40316cffcad5f8f9), |
314 | | (0xbc6bf28dfdd5d37d, 0x3fc2f487fe78b8d7), |
315 | | ]; |
316 | | |
317 | 0 | let x2 = DoubleDouble::quick_mult(recip, recip); |
318 | 0 | let x4 = DoubleDouble::quick_mult(x2, x2); |
319 | 0 | let x8 = DoubleDouble::quick_mult(x4, x4); |
320 | | |
321 | 0 | let e0 = DoubleDouble::mul_add( |
322 | 0 | recip, |
323 | 0 | DoubleDouble::from_bit_pair(P[1]), |
324 | 0 | DoubleDouble::from_bit_pair(P[0]), |
325 | | ); |
326 | 0 | let e1 = DoubleDouble::mul_add( |
327 | 0 | recip, |
328 | 0 | DoubleDouble::from_bit_pair(P[3]), |
329 | 0 | DoubleDouble::from_bit_pair(P[2]), |
330 | | ); |
331 | 0 | let e2 = DoubleDouble::mul_add( |
332 | 0 | recip, |
333 | 0 | DoubleDouble::from_bit_pair(P[5]), |
334 | 0 | DoubleDouble::from_bit_pair(P[4]), |
335 | | ); |
336 | 0 | let e3 = DoubleDouble::mul_add( |
337 | 0 | recip, |
338 | 0 | DoubleDouble::from_bit_pair(P[7]), |
339 | 0 | DoubleDouble::from_bit_pair(P[6]), |
340 | | ); |
341 | 0 | let e4 = DoubleDouble::mul_add( |
342 | 0 | recip, |
343 | 0 | DoubleDouble::from_bit_pair(P[9]), |
344 | 0 | DoubleDouble::from_bit_pair(P[8]), |
345 | | ); |
346 | 0 | let e5 = DoubleDouble::mul_add( |
347 | 0 | recip, |
348 | 0 | DoubleDouble::from_bit_pair(P[11]), |
349 | 0 | DoubleDouble::from_bit_pair(P[10]), |
350 | | ); |
351 | | |
352 | 0 | let f0 = DoubleDouble::mul_add(x2, e1, e0); |
353 | 0 | let f1 = DoubleDouble::mul_add(x2, e3, e2); |
354 | 0 | let f2 = DoubleDouble::mul_add(x2, e5, e4); |
355 | | |
356 | 0 | let g0 = DoubleDouble::mul_add(x4, f1, f0); |
357 | | |
358 | 0 | let p_num = DoubleDouble::mul_add(x8, f2, g0); |
359 | | |
360 | | const Q: [(u64, u64); 12] = [ |
361 | | (0x0000000000000000, 0x3ff0000000000000), |
362 | | (0xbcb9e8a5b17e696a, 0x403a485acd64d64a), |
363 | | (0x3cd2e2e9c87f71f7, 0x4071518092320ecb), |
364 | | (0xbd0d05bdb9431a2f, 0x4097e57e4c22c08e), |
365 | | (0x3d5207068ab19ba9, 0x40b2ebadb2db62f9), |
366 | | (0xbd64e37674083471, 0x40c1c0e4e9d6493d), |
367 | | (0x3d3efb7a9a62b020, 0x40c3b94e8d62cdc7), |
368 | | (0x3d47d6ce80a2114b, 0x40b93c2fd39e868e), |
369 | | (0xbd1dfda61f525861, 0x40a1877a53a7f8d8), |
370 | | (0x3d1236ff523dfcfa, 0x4077c3a10c2827de), |
371 | | (0xbcc889997c9b0fe7, 0x4039a1d80b11c4a1), |
372 | | (0x3c7ded0e8d73dddc, 0x3fdafe4913722123), |
373 | | ]; |
374 | | |
375 | 0 | let e0 = DoubleDouble::mul_add_f64( |
376 | 0 | recip, |
377 | 0 | DoubleDouble::from_bit_pair(Q[1]), |
378 | 0 | f64::from_bits(0x3ff0000000000000), |
379 | | ); |
380 | 0 | let e1 = DoubleDouble::mul_add( |
381 | 0 | recip, |
382 | 0 | DoubleDouble::from_bit_pair(Q[3]), |
383 | 0 | DoubleDouble::from_bit_pair(Q[2]), |
384 | | ); |
385 | 0 | let e2 = DoubleDouble::mul_add( |
386 | 0 | recip, |
387 | 0 | DoubleDouble::from_bit_pair(Q[5]), |
388 | 0 | DoubleDouble::from_bit_pair(Q[4]), |
389 | | ); |
390 | 0 | let e3 = DoubleDouble::mul_add( |
391 | 0 | recip, |
392 | 0 | DoubleDouble::from_bit_pair(Q[7]), |
393 | 0 | DoubleDouble::from_bit_pair(Q[6]), |
394 | | ); |
395 | 0 | let e4 = DoubleDouble::mul_add( |
396 | 0 | recip, |
397 | 0 | DoubleDouble::from_bit_pair(Q[9]), |
398 | 0 | DoubleDouble::from_bit_pair(Q[8]), |
399 | | ); |
400 | 0 | let e5 = DoubleDouble::mul_add( |
401 | 0 | recip, |
402 | 0 | DoubleDouble::from_bit_pair(Q[11]), |
403 | 0 | DoubleDouble::from_bit_pair(Q[10]), |
404 | | ); |
405 | | |
406 | 0 | let f0 = DoubleDouble::mul_add(x2, e1, e0); |
407 | 0 | let f1 = DoubleDouble::mul_add(x2, e3, e2); |
408 | 0 | let f2 = DoubleDouble::mul_add(x2, e5, e4); |
409 | | |
410 | 0 | let g0 = DoubleDouble::mul_add(x4, f1, f0); |
411 | | |
412 | 0 | let p_den = DoubleDouble::mul_add(x8, f2, g0); |
413 | | |
414 | 0 | let z = DoubleDouble::div(p_num, p_den); |
415 | | |
416 | 0 | let r = DoubleDouble::div(z, e * r_sqrt * e); |
417 | | |
418 | 0 | let err = r.hi * f64::from_bits(0x3c10000000000000); // 2^-62 |
419 | 0 | let ub = r.hi + (r.lo + err); |
420 | 0 | let lb = r.hi + (r.lo - err); |
421 | 0 | if ub != lb { |
422 | 0 | return k0_asympt_hard(x); |
423 | 0 | } |
424 | 0 | r.to_f64() |
425 | 0 | } |
426 | | |
427 | | /** |
428 | | Generated in Wolfram |
429 | | |
430 | | Computes sqrt(x)*exp(x)*K0(x)=Pn(1/x)/Qm(1/x) |
431 | | hence |
432 | | K0(x) = Pn(1/x)/Qm(1/x) / (sqrt(x) * exp(x)) |
433 | | |
434 | | ```text |
435 | | <<FunctionApproximations` |
436 | | ClearAll["Global`*"] |
437 | | f[x_]:=Sqrt[x] Exp[x] BesselK[0,x] |
438 | | g[z_]:=f[1/z] |
439 | | {err, approx}=MiniMaxApproximation[g[z],{z,{0.0000000000001,1},14,14},WorkingPrecision->90] |
440 | | poly=Numerator[approx][[1]]; |
441 | | coeffs=CoefficientList[poly,z]; |
442 | | TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]] |
443 | | poly=Denominator[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 | | ``` |
447 | | **/ |
448 | | #[inline(never)] |
449 | | #[cold] |
450 | 0 | fn k0_asympt_hard(x: f64) -> f64 { |
451 | | static P: [DyadicFloat128; 15] = [ |
452 | | DyadicFloat128 { |
453 | | sign: DyadicSign::Pos, |
454 | | exponent: -127, |
455 | | mantissa: 0xa06c98ff_b1382cb2_be520f51_a7b8f970_u128, |
456 | | }, |
457 | | DyadicFloat128 { |
458 | | sign: DyadicSign::Pos, |
459 | | exponent: -122, |
460 | | mantissa: 0xc84d8d0c_7faeef84_e56abccc_3d70f8a2_u128, |
461 | | }, |
462 | | DyadicFloat128 { |
463 | | sign: DyadicSign::Pos, |
464 | | exponent: -118, |
465 | | mantissa: 0xd1a71096_3da22280_35768c9e_0d3ddf42_u128, |
466 | | }, |
467 | | DyadicFloat128 { |
468 | | sign: DyadicSign::Pos, |
469 | | exponent: -115, |
470 | | mantissa: 0xf202e474_3698aabb_05688da0_ea1a088d_u128, |
471 | | }, |
472 | | DyadicFloat128 { |
473 | | sign: DyadicSign::Pos, |
474 | | exponent: -112, |
475 | | mantissa: 0xaaa01830_8138af4d_1137b2dd_11a216f5_u128, |
476 | | }, |
477 | | DyadicFloat128 { |
478 | | sign: DyadicSign::Pos, |
479 | | exponent: -110, |
480 | | mantissa: 0x99e0649f_320bca1a_c7adadb3_f5d8498e_u128, |
481 | | }, |
482 | | DyadicFloat128 { |
483 | | sign: DyadicSign::Pos, |
484 | | exponent: -109, |
485 | | mantissa: 0xb4d81657_de1baf00_918cbc76_c6974e96_u128, |
486 | | }, |
487 | | DyadicFloat128 { |
488 | | sign: DyadicSign::Pos, |
489 | | exponent: -108, |
490 | | mantissa: 0x8a9a28c8_a61c2c7a_12416d56_51c0b3d3_u128, |
491 | | }, |
492 | | DyadicFloat128 { |
493 | | sign: DyadicSign::Pos, |
494 | | exponent: -108, |
495 | | mantissa: 0x88a079f1_d9bd4582_6353316c_3aeb9dc9_u128, |
496 | | }, |
497 | | DyadicFloat128 { |
498 | | sign: DyadicSign::Pos, |
499 | | exponent: -109, |
500 | | mantissa: 0xa82e10eb_9dc6225a_ef6223e7_54aa254d_u128, |
501 | | }, |
502 | | DyadicFloat128 { |
503 | | sign: DyadicSign::Pos, |
504 | | exponent: -111, |
505 | | mantissa: 0xf5fc07fe_6b652e8a_0b9e74ba_d0c56118_u128, |
506 | | }, |
507 | | DyadicFloat128 { |
508 | | sign: DyadicSign::Pos, |
509 | | exponent: -113, |
510 | | mantissa: 0xc5288444_c7354b24_4a4e1663_86488928_u128, |
511 | | }, |
512 | | DyadicFloat128 { |
513 | | sign: DyadicSign::Pos, |
514 | | exponent: -116, |
515 | | mantissa: 0x96d3d226_a220ae6e_d6cca1ae_40f01e27_u128, |
516 | | }, |
517 | | DyadicFloat128 { |
518 | | sign: DyadicSign::Pos, |
519 | | exponent: -121, |
520 | | mantissa: 0xa7ab931b_499c4964_499c1091_4ab9673d_u128, |
521 | | }, |
522 | | DyadicFloat128 { |
523 | | sign: DyadicSign::Pos, |
524 | | exponent: -129, |
525 | | mantissa: 0xf8373d1a_9ff3f9c6_e5cfbe0a_85ccc131_u128, |
526 | | }, |
527 | | ]; |
528 | | |
529 | | static Q: [DyadicFloat128; 15] = [ |
530 | | DyadicFloat128 { |
531 | | sign: DyadicSign::Pos, |
532 | | exponent: -127, |
533 | | mantissa: 0x80000000_00000000_00000000_00000000_u128, |
534 | | }, |
535 | | DyadicFloat128 { |
536 | | sign: DyadicSign::Pos, |
537 | | exponent: -122, |
538 | | mantissa: 0xa05190f4_dcf0d35c_277e0f21_0635c538_u128, |
539 | | }, |
540 | | DyadicFloat128 { |
541 | | sign: DyadicSign::Pos, |
542 | | exponent: -118, |
543 | | mantissa: 0xa8837381_94c38992_86c0995d_5e5fa474_u128, |
544 | | }, |
545 | | DyadicFloat128 { |
546 | | sign: DyadicSign::Pos, |
547 | | exponent: -115, |
548 | | mantissa: 0xc3a4f279_9297e905_f59cc065_75959de8_u128, |
549 | | }, |
550 | | DyadicFloat128 { |
551 | | sign: DyadicSign::Pos, |
552 | | exponent: -112, |
553 | | mantissa: 0x8b05ade4_03432e06_881ce37d_a907216d_u128, |
554 | | }, |
555 | | DyadicFloat128 { |
556 | | sign: DyadicSign::Pos, |
557 | | exponent: -111, |
558 | | mantissa: 0xfd77f85e_35626f21_355ae728_01b78cbe_u128, |
559 | | }, |
560 | | DyadicFloat128 { |
561 | | sign: DyadicSign::Pos, |
562 | | exponent: -109, |
563 | | mantissa: 0x972ed117_254970eb_661121dc_a4462d2f_u128, |
564 | | }, |
565 | | DyadicFloat128 { |
566 | | sign: DyadicSign::Pos, |
567 | | exponent: -109, |
568 | | mantissa: 0xec9d204a_9294ab57_2ef500d5_59d701b7_u128, |
569 | | }, |
570 | | DyadicFloat128 { |
571 | | sign: DyadicSign::Pos, |
572 | | exponent: -109, |
573 | | mantissa: 0xf033522d_cae45860_53a01453_c56da895_u128, |
574 | | }, |
575 | | DyadicFloat128 { |
576 | | sign: DyadicSign::Pos, |
577 | | exponent: -109, |
578 | | mantissa: 0x9a33640c_9896ead5_1ce040d7_b36544f3_u128, |
579 | | }, |
580 | | DyadicFloat128 { |
581 | | sign: DyadicSign::Pos, |
582 | | exponent: -111, |
583 | | mantissa: 0xefe714fa_49da0166_fdf8bc68_57b77fa0_u128, |
584 | | }, |
585 | | DyadicFloat128 { |
586 | | sign: DyadicSign::Pos, |
587 | | exponent: -113, |
588 | | mantissa: 0xd323b84c_214196b0_e25b8931_930fea0d_u128, |
589 | | }, |
590 | | DyadicFloat128 { |
591 | | sign: DyadicSign::Pos, |
592 | | exponent: -116, |
593 | | mantissa: 0xbbb5240b_346642d8_010383cb_1e8a607e_u128, |
594 | | }, |
595 | | DyadicFloat128 { |
596 | | sign: DyadicSign::Pos, |
597 | | exponent: -120, |
598 | | mantissa: 0x88dcfa2a_f9f7d2ab_dd017994_8fae7e87_u128, |
599 | | }, |
600 | | DyadicFloat128 { |
601 | | sign: DyadicSign::Pos, |
602 | | exponent: -127, |
603 | | mantissa: 0xc891477c_526e0f5e_74c4ae9f_9d8732b5_u128, |
604 | | }, |
605 | | ]; |
606 | | |
607 | 0 | let recip = DyadicFloat128::accurate_reciprocal(x); |
608 | 0 | let e = rational128_exp(x); |
609 | 0 | let r_sqrt = bessel_rsqrt_hard(x, recip); |
610 | | |
611 | 0 | let mut p0 = P[14]; |
612 | 0 | for i in (0..14).rev() { |
613 | 0 | p0 = recip * p0 + P[i]; |
614 | 0 | } |
615 | | |
616 | 0 | let mut q = Q[14]; |
617 | 0 | for i in (0..14).rev() { |
618 | 0 | q = recip * q + Q[i]; |
619 | 0 | } |
620 | | |
621 | 0 | let v = p0 * q.reciprocal(); |
622 | 0 | let r = v * e.reciprocal() * r_sqrt; |
623 | 0 | r.fast_as_f64() |
624 | 0 | } |
625 | | |
626 | | #[cfg(test)] |
627 | | mod tests { |
628 | | use super::*; |
629 | | |
630 | | #[test] |
631 | | fn test_k0() { |
632 | | assert_eq!(f_k0(0.11), 2.3332678776741127); |
633 | | assert_eq!(f_k0(0.643), 0.7241025575342853); |
634 | | assert_eq!(f_k0(0.964), 0.4433737413379138); |
635 | | assert_eq!(f_k0(2.964), 0.03621679838808167); |
636 | | assert_eq!(f_k0(423.43), 7.784461905543397e-186); |
637 | | assert_eq!(f_k0(0.), f64::INFINITY); |
638 | | assert_eq!(f_k0(-0.), f64::INFINITY); |
639 | | assert!(f_k0(-0.5).is_nan()); |
640 | | assert!(f_k0(f64::NEG_INFINITY).is_nan()); |
641 | | assert_eq!(f_k0(f64::INFINITY), 0.); |
642 | | } |
643 | | } |