Files
another-boids-in-rust/vendor/pxfm/src/bessel/i0e.rs

835 lines
26 KiB
Rust

/*
* // Copyright (c) Radzivon Bartoshyk 7/2025. All rights reserved.
* //
* // Redistribution and use in source and binary forms, with or without modification,
* // are permitted provided that the following conditions are met:
* //
* // 1. Redistributions of source code must retain the above copyright notice, this
* // list of conditions and the following disclaimer.
* //
* // 2. Redistributions in binary form must reproduce the above copyright notice,
* // this list of conditions and the following disclaimer in the documentation
* // and/or other materials provided with the distribution.
* //
* // 3. Neither the name of the copyright holder nor the names of its
* // contributors may be used to endorse or promote products derived from
* // this software without specific prior written permission.
* //
* // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
use crate::bessel::bessel_exp::i0_exp_accurate;
use crate::bessel::i0::{
bessel_rsqrt_hard, eval_small_hard_3p6_to_7p5, i0_0_to_3p6_dd, i0_0_to_3p6_hard,
i0_3p6_to_7p5_dd,
};
use crate::bessel::i0_exp;
use crate::common::f_fmla;
use crate::double_double::DoubleDouble;
use crate::dyadic_float::{DyadicFloat128, DyadicSign};
/// Modified exponentially scaled Bessel of the first kind of order 0
///
/// Computes exp(-|x|)*I0(x)
pub fn f_i0e(x: f64) -> f64 {
let ux = x.to_bits().wrapping_shl(1);
if ux >= 0x7ffu64 << 53 || ux <= 0x7960000000000000u64 {
// |x| <= f64::EPSILON, |x| == inf, x == NaN
if ux <= 0x760af31dc4611874u64 {
// |x| <= 2.2204460492503131e-24f64
return 1.;
}
if ux <= 0x7960000000000000u64 {
// |x| <= f64::EPSILON
// Power series of I0(x)Exp[-x] ~ 1 - x + O(x^2)
return 1. - x;
}
if x.is_infinite() {
return 0.;
}
return x + f64::NAN; // x = NaN
}
let xb = x.to_bits() & 0x7fff_ffff_ffff_ffff;
if xb <= 0x4023000000000000u64 {
// |x| <= 9.5
if xb <= 0x400ccccccccccccdu64 {
// |x| <= 3.6
return i0e_0_to_3p6_exec(f64::from_bits(xb));
} else if xb <= 0x401e000000000000u64 {
// |x| <= 7.5
return i0e3p6_to_7p5(f64::from_bits(xb));
}
return i0e_7p5_to_9p5(f64::from_bits(xb));
}
i0e_asympt(f64::from_bits(xb))
}
/**
Computes I0 on interval [-7.5; -3.6], [3.6; 7.5]
**/
#[inline]
fn i0e3p6_to_7p5(x: f64) -> f64 {
let mut r = i0_3p6_to_7p5_dd(x);
let v_exp = i0_exp(-x);
r = DoubleDouble::quick_mult(r, v_exp);
let err = f_fmla(
r.hi,
f64::from_bits(0x3c56a09e667f3bcd), // 2^-57.5
f64::from_bits(0x3c00000000000000), // 2^-63
);
let ub = r.hi + (r.lo + err);
let lb = r.hi + (r.lo - err);
if ub == lb {
return ub;
}
let v = eval_small_hard_3p6_to_7p5(x);
let v_exp_accurate = i0_exp_accurate(-x);
DoubleDouble::quick_mult(v, v_exp_accurate).to_f64()
}
#[inline]
fn i0e_0_to_3p6_exec(x: f64) -> f64 {
let mut r = i0_0_to_3p6_dd(x);
let v_exp = i0_exp(-x);
r = DoubleDouble::quick_mult(r, v_exp);
let err = f_fmla(
r.hi,
f64::from_bits(0x3c40000000000000), // 2^-59
f64::from_bits(0x3be0000000000000), // 2^-66
);
let ub = r.hi + (r.lo + err);
let lb = r.hi + (r.lo - err);
if ub == lb {
return ub;
}
let v = i0_0_to_3p6_hard(x);
let v_exp_accurate = i0_exp_accurate(-x);
DoubleDouble::quick_mult(v, v_exp_accurate).to_f64()
}
/**
Mid-interval [7.5;9.5] generated by Wolfram:
I0(x)=R(1/x)/sqrt(x)*Exp(x)
```text
<<FunctionApproximations`
ClearAll["Global`*"]
f[x_]:=Sqrt[x] Exp[-x] BesselI[0,x]
g[z_]:=f[1/z]
{err, approx}=MiniMaxApproximation[g[z],{z,{1/9.5,1/7.5},11,11},WorkingPrecision->120]
num=Numerator[approx][[1]];
den=Denominator[approx][[1]];
poly=den;
coeffs=CoefficientList[poly,z];
TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
```
**/
#[inline]
fn i0e_7p5_to_9p5(x: f64) -> f64 {
let dx = x;
let recip = DoubleDouble::from_quick_recip(x);
const P: [(u64, u64); 12] = [
(0x3c778e3de1f76f48, 0x3fd988450531281b),
(0xbcb572f6149f389e, 0xc01a786676fb4d3a),
(0x3cf2f373365347ed, 0x405c0e8405fdb642),
(0x3d276a94c8f1e627, 0xc0885e4718dfb761),
(0x3d569f8a993434e2, 0x40b756d52d5fa90c),
(0xbd6f953f7dd1a223, 0xc0c8818365c47790),
(0xbd74247967fbf7b2, 0x40e8cf89daf87353),
(0x3db449add7abb056, 0x41145d3c2d96d159),
(0xbdc5cc822b71f891, 0xc123694c58fd039b),
(0x3da2047ac1a6fba8, 0x415462e630bf3e7e),
(0xbdc2f2c06eda6a95, 0xc14c6984ebdd6792),
(0xbdf51fa85dafeca5, 0x4166a437c202d27b),
];
let x2 = DoubleDouble::quick_mult(recip, recip);
let x4 = DoubleDouble::quick_mult(x2, x2);
let x8 = DoubleDouble::quick_mult(x4, x4);
let e0 = DoubleDouble::mul_add(
recip,
DoubleDouble::from_bit_pair(P[1]),
DoubleDouble::from_bit_pair(P[0]),
);
let e1 = DoubleDouble::mul_add(
recip,
DoubleDouble::from_bit_pair(P[3]),
DoubleDouble::from_bit_pair(P[2]),
);
let e2 = DoubleDouble::mul_add(
recip,
DoubleDouble::from_bit_pair(P[5]),
DoubleDouble::from_bit_pair(P[4]),
);
let e3 = DoubleDouble::mul_add(
recip,
DoubleDouble::from_bit_pair(P[7]),
DoubleDouble::from_bit_pair(P[6]),
);
let e4 = DoubleDouble::mul_add(
recip,
DoubleDouble::from_bit_pair(P[9]),
DoubleDouble::from_bit_pair(P[8]),
);
let e5 = DoubleDouble::mul_add(
recip,
DoubleDouble::from_bit_pair(P[11]),
DoubleDouble::from_bit_pair(P[10]),
);
let f0 = DoubleDouble::mul_add(x2, e1, e0);
let f1 = DoubleDouble::mul_add(x2, e3, e2);
let f2 = DoubleDouble::mul_add(x2, e5, e4);
let g0 = DoubleDouble::mul_add(x4, f1, f0);
let p_num = DoubleDouble::mul_add(x8, f2, g0);
const Q: [(u64, u64); 12] = [
(0x0000000000000000, 0x3ff0000000000000),
(0x3cde08e4cbf324d1, 0xc030b67bd69af0ca),
(0x3cec5e4ee7e77024, 0x4071b54c0f58409c),
(0xbd340e1131739e2f, 0xc09f140a738b14b3),
(0x3d607673189d6644, 0x40cdb44bd822add2),
(0xbd7793a4f1dd74d1, 0xc0e03fe2689b802d),
(0xbd8415501228a87e, 0x410009beafea72cc),
(0x3dcecdac2702661f, 0x4128c2073da9a447),
(0xbdd8386404f3dec5, 0xc1389ec7d7186bf4),
(0xbe06eb53a3e86436, 0x4168b7a2dc85ed0b),
(0x3e098e2cefaf8299, 0xc1604f8cf34af02c),
(0x3e1a5e496b547001, 0x41776b1e0153d1e9),
];
let e0 = DoubleDouble::mul_add_f64(
recip,
DoubleDouble::from_bit_pair(Q[1]),
f64::from_bits(0x3ff0000000000000),
);
let e1 = DoubleDouble::mul_add(
recip,
DoubleDouble::from_bit_pair(Q[3]),
DoubleDouble::from_bit_pair(Q[2]),
);
let e2 = DoubleDouble::mul_add(
recip,
DoubleDouble::from_bit_pair(Q[5]),
DoubleDouble::from_bit_pair(Q[4]),
);
let e3 = DoubleDouble::mul_add(
recip,
DoubleDouble::from_bit_pair(Q[7]),
DoubleDouble::from_bit_pair(Q[6]),
);
let e4 = DoubleDouble::mul_add(
recip,
DoubleDouble::from_bit_pair(Q[9]),
DoubleDouble::from_bit_pair(Q[8]),
);
let e5 = DoubleDouble::mul_add(
recip,
DoubleDouble::from_bit_pair(Q[11]),
DoubleDouble::from_bit_pair(Q[10]),
);
let f0 = DoubleDouble::mul_add(x2, e1, e0);
let f1 = DoubleDouble::mul_add(x2, e3, e2);
let f2 = DoubleDouble::mul_add(x2, e5, e4);
let g0 = DoubleDouble::mul_add(x4, f1, f0);
let p_den = DoubleDouble::mul_add(x8, f2, g0);
let z = DoubleDouble::div(p_num, p_den);
let r_sqrt = DoubleDouble::from_rsqrt_fast(dx);
let r = z * r_sqrt;
let err = f_fmla(
r.hi,
f64::from_bits(0x3bc0000000000000),
f64::from_bits(0x392bdb8cdadbe111),
);
let up = r.hi + (r.lo + err);
let lb = r.hi + (r.lo - err);
if up != lb {
return i0e_7p5_to_9p5_hard(x);
}
r.to_f64()
}
/**
Mid-interval [7.5;9.5] generated by Wolfram Mathematica:
I0(x)=R(1/x)/sqrt(x)*Exp(x)
Polynomial generated by Wolfram Mathematica:
```text
<<FunctionApproximations`
ClearAll["Global`*"]
f[x_]:=Sqrt[x] Exp[-x] BesselI[0,x]
g[z_]:=f[1/z]
{err,approx}=MiniMaxApproximation[g[z],{z,{1/9.5,1/7.5},13,13},WorkingPrecision->120]
poly=Numerator[approx][[1]];
coeffs=CoefficientList[poly,z];
TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
poly=Denominator[approx][[1]];
coeffs=CoefficientList[poly,z];
TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
```
**/
#[cold]
#[inline(never)]
fn i0e_7p5_to_9p5_hard(x: f64) -> f64 {
static P: [DyadicFloat128; 14] = [
DyadicFloat128 {
sign: DyadicSign::Pos,
exponent: -129,
mantissa: 0xcc422a04_45cde144_75a3800b_45c38460_u128,
},
DyadicFloat128 {
sign: DyadicSign::Neg,
exponent: -124,
mantissa: 0xada66144_fcccc1a3_036f76b2_cabd6281_u128,
},
DyadicFloat128 {
sign: DyadicSign::Pos,
exponent: -120,
mantissa: 0xeabdda02_fa201d98_10e58d1f_7eb62bd7_u128,
},
DyadicFloat128 {
sign: DyadicSign::Neg,
exponent: -116,
mantissa: 0xbbfd3297_6f88a7df_5924587b_d5bdcdb8_u128,
},
DyadicFloat128 {
sign: DyadicSign::Pos,
exponent: -113,
mantissa: 0xfca29453_efe393bf_1651627b_7d543875_u128,
},
DyadicFloat128 {
sign: DyadicSign::Neg,
exponent: -110,
mantissa: 0xee7c7220_bbbd248e_bb6adac6_f9a5ce95_u128,
},
DyadicFloat128 {
sign: DyadicSign::Pos,
exponent: -107,
mantissa: 0xc07455dd_830ba705_414408c6_06732a5a_u128,
},
DyadicFloat128 {
sign: DyadicSign::Neg,
exponent: -105,
mantissa: 0xe2247793_b50cd0f0_80e8981d_933f75da_u128,
},
DyadicFloat128 {
sign: DyadicSign::Pos,
exponent: -103,
mantissa: 0xe14a9831_82582a0b_dd27e8b6_4ed9aac2_u128,
},
DyadicFloat128 {
sign: DyadicSign::Neg,
exponent: -101,
mantissa: 0xa3b2ae2f_5b64f37e_c1538435_34f02faf_u128,
},
DyadicFloat128 {
sign: DyadicSign::Pos,
exponent: -100,
mantissa: 0xbab73503_5b7e38d9_bbe4a84b_9007c6e8_u128,
},
DyadicFloat128 {
sign: DyadicSign::Neg,
exponent: -99,
mantissa: 0xa68911fc_5d87bbe7_0d4fe854_5c681ac5_u128,
},
DyadicFloat128 {
sign: DyadicSign::Pos,
exponent: -99,
mantissa: 0x9e997222_55ef4045_fa9f311d_57d082a2_u128,
},
DyadicFloat128 {
sign: DyadicSign::Neg,
exponent: -99,
mantissa: 0xbe93656a_b0a4f32d_3ebbfdeb_b1cbb839_u128,
},
];
static Q: [DyadicFloat128; 14] = [
DyadicFloat128 {
sign: DyadicSign::Pos,
exponent: -127,
mantissa: 0x80000000_00000000_00000000_00000000_u128,
},
DyadicFloat128 {
sign: DyadicSign::Neg,
exponent: -123,
mantissa: 0xdaa34a7e_861dddff_a0642080_cd83dd65_u128,
},
DyadicFloat128 {
sign: DyadicSign::Pos,
exponent: -118,
mantissa: 0x93f05740_f4758772_bb9992f9_91e72795_u128,
},
DyadicFloat128 {
sign: DyadicSign::Neg,
exponent: -115,
mantissa: 0xeddcb810_054c9aab_fa7e4214_d59d18b0_u128,
},
DyadicFloat128 {
sign: DyadicSign::Pos,
exponent: -111,
mantissa: 0xa0180fcd_831ff6c0_ac2b8f02_37f3cfd1_u128,
},
DyadicFloat128 {
sign: DyadicSign::Neg,
exponent: -108,
mantissa: 0x97d25106_3b66907e_90b4f786_26daa0bb_u128,
},
DyadicFloat128 {
sign: DyadicSign::Pos,
exponent: -106,
mantissa: 0xf595ce38_aac16c11_001b874a_99603b45_u128,
},
DyadicFloat128 {
sign: DyadicSign::Neg,
exponent: -103,
mantissa: 0x912b3715_4aca68f6_5821c2ed_43d77111_u128,
},
DyadicFloat128 {
sign: DyadicSign::Pos,
exponent: -101,
mantissa: 0x90f97141_b896e2b6_38b87354_8945a43c_u128,
},
DyadicFloat128 {
sign: DyadicSign::Neg,
exponent: -100,
mantissa: 0xd3e5f967_89097d6b_3a3060fe_852ff580_u128,
},
DyadicFloat128 {
sign: DyadicSign::Pos,
exponent: -99,
mantissa: 0xf0d6de35_939da009_9ced21fd_48af7281_u128,
},
DyadicFloat128 {
sign: DyadicSign::Neg,
exponent: -98,
mantissa: 0xd2a0b183_6ac613b2_6745ce1d_8ed1c323_u128,
},
DyadicFloat128 {
sign: DyadicSign::Pos,
exponent: -98,
mantissa: 0xbb9c089a_b7e939a2_732b3fb5_2e66cd77_u128,
},
DyadicFloat128 {
sign: DyadicSign::Neg,
exponent: -98,
mantissa: 0xcb2107c3_736bef81_609718c0_ba82cd8e_u128,
},
];
let recip = DyadicFloat128::accurate_reciprocal(x);
let mut p_num = P[13];
for i in (0..13).rev() {
p_num = recip * p_num + P[i];
}
let mut p_den = Q[13];
for i in (0..13).rev() {
p_den = recip * p_den + Q[i];
}
let z = p_num * p_den.reciprocal();
let r_sqrt = bessel_rsqrt_hard(x, recip);
(z * r_sqrt).fast_as_f64()
}
/**
I0(x)=R(1/x)*Exp(x)/sqrt(x)
Generated in Wolfram:
```text
<<FunctionApproximations`
ClearAll["Global`*"]
f[x_]:=Sqrt[x] Exp[-x] BesselI[0,x]
g[z_]:=f[1/z]
{err,approx, err1}=MiniMaxApproximation[g[z],{z,{1/709.3,1/9.5},11,11},WorkingPrecision->120]
poly=Numerator[approx];
coeffs=CoefficientList[poly,z];
TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
poly=Denominator[approx];
coeffs=CoefficientList[poly,z];
TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]]
```
**/
#[inline]
fn i0e_asympt(x: f64) -> f64 {
let dx = x;
let recip = DoubleDouble::from_quick_recip(x);
const P: [(u64, u64); 12] = [
(0xbc7ca19c5d824c54, 0x3fd9884533d43651),
(0x3cca32eb734e010e, 0xc03b7ca35caf42eb),
(0x3d03af8238d6f25e, 0x408b92cfcaa7070f),
(0xbd7a8ff7fdebed70, 0xc0d0a3be432cce93),
(0xbdababdb579bb076, 0x410a77dc51f1804d),
(0x3dc5e4e3c972832a, 0xc13cb0be2f74839c),
(0x3e01076f9b102e38, 0x41653b064cc61661),
(0xbe2157e700d445f4, 0xc184e1b076927460),
(0xbdfa4577156dde56, 0x41999e9653f9dc5f),
(0xbe47aa238a739ffe, 0xc1a130f6ded40c00),
(0xbe331b560b6fbf4a, 0x419317f11e674cae),
(0xbe0765596077d1e3, 0xc16024404db59d3f),
];
let x2 = DoubleDouble::quick_mult(recip, recip);
let x4 = DoubleDouble::quick_mult(x2, x2);
let x8 = DoubleDouble::quick_mult(x4, x4);
let e0 = DoubleDouble::mul_add(
recip,
DoubleDouble::from_bit_pair(P[1]),
DoubleDouble::from_bit_pair(P[0]),
);
let e1 = DoubleDouble::mul_add(
recip,
DoubleDouble::from_bit_pair(P[3]),
DoubleDouble::from_bit_pair(P[2]),
);
let e2 = DoubleDouble::mul_add(
recip,
DoubleDouble::from_bit_pair(P[5]),
DoubleDouble::from_bit_pair(P[4]),
);
let e3 = DoubleDouble::mul_add(
recip,
DoubleDouble::from_bit_pair(P[7]),
DoubleDouble::from_bit_pair(P[6]),
);
let e4 = DoubleDouble::mul_add(
recip,
DoubleDouble::from_bit_pair(P[9]),
DoubleDouble::from_bit_pair(P[8]),
);
let e5 = DoubleDouble::mul_add(
recip,
DoubleDouble::from_bit_pair(P[11]),
DoubleDouble::from_bit_pair(P[10]),
);
let f0 = DoubleDouble::mul_add(x2, e1, e0);
let f1 = DoubleDouble::mul_add(x2, e3, e2);
let f2 = DoubleDouble::mul_add(x2, e5, e4);
let g0 = DoubleDouble::mul_add(x4, f1, f0);
let p_num = DoubleDouble::mul_add(x8, f2, g0);
const Q: [(u64, u64); 12] = [
(0x0000000000000000, 0x3ff0000000000000),
(0xbcf687703e843d07, 0xc051418f1c4dd0b9),
(0x3d468ab92cb87a0b, 0x40a15891d823e48d),
(0x3d8bfc17e5183376, 0xc0e4fce40dd82796),
(0xbdccbbcc2ecf8d4c, 0x4120beef869c61ec),
(0xbdf42170b4d5d150, 0xc1523ad18834a7ed),
(0xbe0eaa32f405afd4, 0x417b24ec57a8f480),
(0x3e3ec900705e7252, 0xc19af2a91d23d62e),
(0x3e3e220e274fa46b, 0x41b0cb905cc99ff5),
(0xbe46c6c61dee11f6, 0xc1b7452e50518520),
(0x3e3ed0fc063187bf, 0x41ac1772d1749896),
(0xbe11c578f04f4be1, 0xc180feb5b2ca47cb),
];
let e0 = DoubleDouble::mul_add_f64(
recip,
DoubleDouble::from_bit_pair(Q[1]),
f64::from_bits(0x3ff0000000000000),
);
let e1 = DoubleDouble::mul_add(
recip,
DoubleDouble::from_bit_pair(Q[3]),
DoubleDouble::from_bit_pair(Q[2]),
);
let e2 = DoubleDouble::mul_add(
recip,
DoubleDouble::from_bit_pair(Q[5]),
DoubleDouble::from_bit_pair(Q[4]),
);
let e3 = DoubleDouble::mul_add(
recip,
DoubleDouble::from_bit_pair(Q[7]),
DoubleDouble::from_bit_pair(Q[6]),
);
let e4 = DoubleDouble::mul_add(
recip,
DoubleDouble::from_bit_pair(Q[9]),
DoubleDouble::from_bit_pair(Q[8]),
);
let e5 = DoubleDouble::mul_add(
recip,
DoubleDouble::from_bit_pair(Q[11]),
DoubleDouble::from_bit_pair(Q[10]),
);
let f0 = DoubleDouble::mul_add(x2, e1, e0);
let f1 = DoubleDouble::mul_add(x2, e3, e2);
let f2 = DoubleDouble::mul_add(x2, e5, e4);
let g0 = DoubleDouble::mul_add(x4, f1, f0);
let p_den = DoubleDouble::mul_add(x8, f2, g0);
let z = DoubleDouble::div(p_num, p_den);
let r_sqrt = DoubleDouble::from_rsqrt_fast(dx);
let r = z * r_sqrt;
let err = f_fmla(
r.hi,
f64::from_bits(0x3c40000000000000), // 2^-59
f64::from_bits(0x3be0000000000000), // 2^-65
);
let up = r.hi + (r.lo + err);
let lb = r.hi + (r.lo - err);
if up != lb {
return i0e_asympt_hard(x);
}
lb
}
/**
I0(x)=R(1/x)*Exp(x)/sqrt(x)
Generated in Wolfram:
```text
<<FunctionApproximations`
ClearAll["Global`*"]
f[x_]:=Sqrt[x] Exp[-x] BesselI[0,x]
g[z_]:=f[1/z]
{err,approx, err1}=MiniMaxApproximation[g[z],{z,{1/709.3,1/9.5},15,15},WorkingPrecision->120]
poly=Numerator[approx];
coeffs=CoefficientList[poly,z];
TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
poly=Denominator[approx];
coeffs=CoefficientList[poly,z];
TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
```
**/
#[cold]
#[inline(never)]
fn i0e_asympt_hard(x: f64) -> f64 {
static P: [DyadicFloat128; 16] = [
DyadicFloat128 {
sign: DyadicSign::Pos,
exponent: -129,
mantissa: 0xcc42299e_a1b28468_7e5aad4a_70b749c4_u128,
},
DyadicFloat128 {
sign: DyadicSign::Neg,
exponent: -122,
mantissa: 0xabb4209d_ca11bdaa_186bef7f_390e6b77_u128,
},
DyadicFloat128 {
sign: DyadicSign::Pos,
exponent: -116,
mantissa: 0x8a2725e2_4749db25_625ad1f2_12a2a16c_u128,
},
DyadicFloat128 {
sign: DyadicSign::Neg,
exponent: -111,
mantissa: 0x8b4c2cd4_9e5d0c8b_c9be4d3e_781bb676_u128,
},
DyadicFloat128 {
sign: DyadicSign::Pos,
exponent: -107,
mantissa: 0xc33fad7c_40599f7d_713e3081_6b5ad791_u128,
},
DyadicFloat128 {
sign: DyadicSign::Neg,
exponent: -103,
mantissa: 0xc81da271_b623ba88_0be032b5_827d92fa_u128,
},
DyadicFloat128 {
sign: DyadicSign::Pos,
exponent: -99,
mantissa: 0x99ec4975_b6aa7cae_7692a287_ed8ae47c_u128,
},
DyadicFloat128 {
sign: DyadicSign::Neg,
exponent: -96,
mantissa: 0xb3aa4745_fc2dd441_2dbd3e3c_d4539687_u128,
},
DyadicFloat128 {
sign: DyadicSign::Pos,
exponent: -93,
mantissa: 0x9f14edc2_6882afca_29d2a067_dc459729_u128,
},
DyadicFloat128 {
sign: DyadicSign::Neg,
exponent: -91,
mantissa: 0xd35c4d01_78d8cec6_fc8ae0ee_834da837_u128,
},
DyadicFloat128 {
sign: DyadicSign::Pos,
exponent: -89,
mantissa: 0xcdc529c7_6e082342_faad3073_07a9b61f_u128,
},
DyadicFloat128 {
sign: DyadicSign::Neg,
exponent: -87,
mantissa: 0x8ccac88f_2598c8a6_423b1f42_63591cb9_u128,
},
DyadicFloat128 {
sign: DyadicSign::Pos,
exponent: -87,
mantissa: 0xfc044cfb_a20f0885_93d58660_17819ed5_u128,
},
DyadicFloat128 {
sign: DyadicSign::Neg,
exponent: -86,
mantissa: 0x813a700c_74d23f52_f81b179d_7ff0da9f_u128,
},
DyadicFloat128 {
sign: DyadicSign::Pos,
exponent: -88,
mantissa: 0xe6c43da4_297216bf_bdd987cb_636906cf_u128,
},
DyadicFloat128 {
sign: DyadicSign::Neg,
exponent: -91,
mantissa: 0xa4998323_649c3cf2_64477869_3d2c6afd_u128,
},
];
static Q: [DyadicFloat128; 16] = [
DyadicFloat128 {
sign: DyadicSign::Pos,
exponent: -127,
mantissa: 0x80000000_00000000_00000000_00000000_u128,
},
DyadicFloat128 {
sign: DyadicSign::Neg,
exponent: -121,
mantissa: 0xd772d5fd_a7077638_6e007274_d83b013c_u128,
},
DyadicFloat128 {
sign: DyadicSign::Pos,
exponent: -115,
mantissa: 0xad914ef0_451ced2e_515657ef_fc7eeb53_u128,
},
DyadicFloat128 {
sign: DyadicSign::Neg,
exponent: -110,
mantissa: 0xaf41180c_dffe96e5_f192fa40_0b1bff1e_u128,
},
DyadicFloat128 {
sign: DyadicSign::Pos,
exponent: -106,
mantissa: 0xf60dc728_241f71fd_5b93e653_ccbedace_u128,
},
DyadicFloat128 {
sign: DyadicSign::Neg,
exponent: -102,
mantissa: 0xfcaefef9_39cf96e7_3cb75a98_da5e9761_u128,
},
DyadicFloat128 {
sign: DyadicSign::Pos,
exponent: -98,
mantissa: 0xc2d2c837_5789587a_13ef38c6_a24c3413_u128,
},
DyadicFloat128 {
sign: DyadicSign::Neg,
exponent: -95,
mantissa: 0xe41725c3_51d14486_a650044e_e8588f7b_u128,
},
DyadicFloat128 {
sign: DyadicSign::Pos,
exponent: -92,
mantissa: 0xcabeed9b_5e2e888d_81d32b11_d289a624_u128,
},
DyadicFloat128 {
sign: DyadicSign::Neg,
exponent: -89,
mantissa: 0x8764876d_11ad6607_8a8d5382_3ffe82d9_u128,
},
DyadicFloat128 {
sign: DyadicSign::Pos,
exponent: -87,
mantissa: 0x84c9f9e5_6a5f5034_ad2c8512_16cb1ba1_u128,
},
DyadicFloat128 {
sign: DyadicSign::Neg,
exponent: -86,
mantissa: 0xb7c1d143_a15d8aab_03a7fa3e_b7d07a36_u128,
},
DyadicFloat128 {
sign: DyadicSign::Pos,
exponent: -85,
mantissa: 0xa78f8257_4658040f_7a1ad39c_91ea9483_u128,
},
DyadicFloat128 {
sign: DyadicSign::Neg,
exponent: -85,
mantissa: 0xb231e0ca_b729a404_44c38f52_be208507_u128,
},
DyadicFloat128 {
sign: DyadicSign::Pos,
exponent: -86,
mantissa: 0xae317981_42349081_8bc68b28_f69b8e49_u128,
},
DyadicFloat128 {
sign: DyadicSign::Neg,
exponent: -89,
mantissa: 0xb451abd3_5cd79c6d_7e578164_32f16da1_u128,
},
];
let recip = DyadicFloat128::accurate_reciprocal(x);
let mut p_num = P[15];
for i in (0..15).rev() {
p_num = recip * p_num + P[i];
}
let mut p_den = Q[15];
for i in (0..15).rev() {
p_den = recip * p_den + Q[i];
}
let z = p_num * p_den.reciprocal();
let r_sqrt = bessel_rsqrt_hard(x, recip);
(z * r_sqrt).fast_as_f64()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_i0e() {
assert_eq!(f_i0e(0.00000000000000000000000000052342), 1.0);
assert_eq!(f_i0e(f64::EPSILON), 0.9999999999999998);
assert_eq!(f_i0e(9.500000000005492,), 0.13125126081422883);
assert!(f_i0e(f64::NAN).is_nan());
assert_eq!(f_i0e(f64::INFINITY), 0.);
assert_eq!(f_i0e(f64::NEG_INFINITY), 0.);
assert_eq!(f_i0e(7.500000000788034), 0.14831583006929877);
assert_eq!(f_i0e(715.), 0.014922205745802662);
assert_eq!(f_i0e(12.), 0.11642622121344044);
assert_eq!(f_i0e(16.), 0.10054412736125203);
assert_eq!(f_i0e(18.432), 0.09357372647647);
assert_eq!(f_i0e(26.432), 0.07797212360059241);
assert_eq!(f_i0e(0.2), 0.8269385516343293);
assert_eq!(f_i0e(7.5), 0.14831583007739552);
assert_eq!(f_i0e(-1.5), 0.36743360905415834);
assert_eq!(i0e_asympt_hard(18.432), 0.09357372647647);
}
}