/* * // 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 <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 <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 <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 <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); } }