use crate::bessel::i0::bessel_rsqrt_hard;
use crate::bessel::i0_exp;
use crate::common::f_fmla;
use crate::double_double::DoubleDouble;
use crate::dyadic_float::{DyadicFloat128, DyadicSign};
use crate::exponents::rational128_exp;
pub fn f_i2(x: f64) -> f64 {
let ux = x.to_bits().wrapping_shl(1);
if ux >= 0x7ffu64 << 53 || ux == 0 {
if ux == 0 {
return 0.;
}
if x.is_infinite() {
return f64::INFINITY;
}
return x + f64::NAN; }
let xb = x.to_bits() & 0x7fff_ffff_ffff_ffffu64;
if xb < 0x401f000000000000u64 {
if xb <= 0x3cb0000000000000u64 {
const R: f64 = 1. / 8.;
let x2 = x * x * R;
return x2;
}
return i2_small(f64::from_bits(xb));
}
if xb >= 0x40864feaeefb23b8 {
return f64::INFINITY;
}
i2_asympt(f64::from_bits(xb))
}
#[inline]
fn i2_small(x: f64) -> f64 {
const P: [(u64, u64); 12] = [
(0x0000000000000000, 0x3fc0000000000000),
(0x3c247833fda9de9a, 0x3f8387c6e72a1b5f),
(0xbbccaf0be91261a6, 0x3f30ba88efff56fa),
(0x3b57c911bfebe1d7, 0x3ecc62e53d061300),
(0x3af3b963f26a3d05, 0x3e5bb090327a14e1),
(0x3a898bff9d40e030, 0x3de0d29c3d37e5b5),
(0xb9f2f63c80d377db, 0x3d5a9e365f1bf6e0),
(0xb965e6d78e1c2b65, 0x3ccbf7ef0929b813),
(0xb8da83d7d40e7310, 0x3c33737520046f4d),
(0xb83f811d5aa3f36e, 0x3b91506558dab318),
(0xb78e601bf5c998c3, 0x3ae2013b3e858bd1),
(0xb6c8185c51734ed8, 0x3a20cc277a5051ba),
];
let x_sqr = DoubleDouble::from_exact_mult(x, x);
let x2 = x_sqr * x_sqr;
let x4 = x2 * x2;
let x8 = x4 * x4;
let e0 = DoubleDouble::mul_add_f64(
x_sqr,
DoubleDouble::from_bit_pair(P[1]),
f64::from_bits(0x3fc0000000000000),
);
let e1 = DoubleDouble::mul_add(
x_sqr,
DoubleDouble::from_bit_pair(P[3]),
DoubleDouble::from_bit_pair(P[2]),
);
let e2 = DoubleDouble::mul_add(
x_sqr,
DoubleDouble::from_bit_pair(P[5]),
DoubleDouble::from_bit_pair(P[4]),
);
let e3 = DoubleDouble::mul_add(
x_sqr,
DoubleDouble::from_bit_pair(P[7]),
DoubleDouble::from_bit_pair(P[6]),
);
let e4 = DoubleDouble::mul_add(
x_sqr,
DoubleDouble::from_bit_pair(P[9]),
DoubleDouble::from_bit_pair(P[8]),
);
let e5 = DoubleDouble::mul_add(
x_sqr,
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),
(0xbc0ba42af56ed76b, 0xbf7cd8e6e2b39f60),
(0x3b90697aa005e598, 0x3efa0260394e1a3d),
(0xbb0c7ccde1f63c82, 0xbe6f1766ec64e492),
(0x3a63f18409bc336f, 0x3ddb80b6b5abad98),
(0xb9e0cd49f22132fe, 0xbd42ff9b55d553da),
(0xb934bfe64905d309, 0x3ca50814fa258ebc),
(0x38a1e35c2d6860f4, 0xbc02c4f2faca2195),
(0xb7ff39e268277e4e, 0x3b5aa545a2c1f16d),
(0xb71053f58545760c, 0xbaacde4c133d42d1),
(0xb68d0c2ccab0ae5b, 0x39f5a965b92b06bc),
(0xb5dc35bda16bee7b, 0xb931375b1c9cfbc7),
];
let e0 = DoubleDouble::mul_add_f64(
x_sqr,
DoubleDouble::from_bit_pair(Q[1]),
f64::from_bits(0x3ff0000000000000),
);
let e1 = DoubleDouble::mul_add(
x_sqr,
DoubleDouble::from_bit_pair(Q[3]),
DoubleDouble::from_bit_pair(Q[2]),
);
let e2 = DoubleDouble::mul_add(
x_sqr,
DoubleDouble::from_bit_pair(Q[5]),
DoubleDouble::from_bit_pair(Q[4]),
);
let e3 = DoubleDouble::mul_add(
x_sqr,
DoubleDouble::from_bit_pair(Q[7]),
DoubleDouble::from_bit_pair(Q[6]),
);
let e4 = DoubleDouble::mul_add(
x_sqr,
DoubleDouble::from_bit_pair(Q[9]),
DoubleDouble::from_bit_pair(Q[8]),
);
let e5 = DoubleDouble::mul_add(
x_sqr,
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 p = DoubleDouble::div(p_num, p_den);
DoubleDouble::quick_mult(p, x_sqr).to_f64()
}
#[inline]
fn i2_asympt(x: f64) -> f64 {
let dx = x;
let recip = DoubleDouble::from_quick_recip(x);
const P: [(u64, u64); 12] = [
(0x3c718bb28ebc5f4e, 0x3fd9884533d43650),
(0x3c96e15a87b6e1d1, 0xc0350acc9e5cb0f9),
(0xbd20b212a79e08f5, 0x40809251af67598a),
(0xbd563b7397df3a54, 0xc0bfc09ede682c8b),
(0xbd5eb872cb057d91, 0x40f44253a9e1e1ab),
(0x3d7614735e566fc5, 0xc121cbcd96dc8765),
(0xbddc4f8df2010026, 0x4145a592e8ec74ad),
(0x3dea227617b678a7, 0xc161df96fb6a9df9),
(0x3e17c9690d906194, 0x41732c71397757f8),
(0x3e0638226ce0b938, 0xc178893fde0e6ed7),
(0xbe09d8dc4e7930ce, 0x417066abe24b31df),
(0xbde152007ee29e54, 0xc150531da3f31b16),
];
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),
(0xbcd0d33e9e73b503, 0xc0496f5a09751d50),
(0x3d2f9c44a069dc4b, 0x40934427187ac370),
(0xbd69e2e5a3618381, 0xc0d19983f74fdf52),
(0x3d88c69a62ae8b44, 0x410524fcaa71e85a),
(0xbdc0345b806dd0bf, 0xc13120daf531b66b),
(0xbdd35875712fff6f, 0x4152943a4f9f1c7f),
(0xbdf8dd50e92553fd, 0xc169b83aeede08ea),
(0x3e0800ecaa77f79e, 0x41746c61554a08ce),
(0x3dd74fbc32c5f696, 0xc16ba2febd1932a3),
(0x3dc23eb2c943b539, 0x413574ae68b6b378),
(0xbd95d86c5c94cd65, 0xc104adac99eaa90c),
];
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 mut e = i0_exp(dx * 0.5);
e = DoubleDouble::from_exact_add(e.hi, e.lo);
let r_sqrt = DoubleDouble::from_rsqrt_fast(dx);
let r = DoubleDouble::quick_mult(z * r_sqrt * e, e);
let err = f_fmla(
r.hi,
f64::from_bits(0x3c40000000000000), f64::from_bits(0x3ba0000000000000), );
let up = r.hi + (r.lo + err);
let lb = r.hi + (r.lo - err);
if up == lb {
return r.to_f64();
}
i2_asympt_hard(x)
}
#[cold]
#[inline(never)]
fn i2_asympt_hard(x: f64) -> f64 {
static P: [DyadicFloat128; 16] = [
DyadicFloat128 {
sign: DyadicSign::Pos,
exponent: -129,
mantissa: 0xcc42299e_a1b28468_3bb16645_ba1dc793_u128,
},
DyadicFloat128 {
sign: DyadicSign::Neg,
exponent: -123,
mantissa: 0xe202abf7_de10e93f_2a2e6a0f_af69c788_u128,
},
DyadicFloat128 {
sign: DyadicSign::Pos,
exponent: -118,
mantissa: 0xf70296c3_ad33bde6_866cfd01_0e846cfc_u128,
},
DyadicFloat128 {
sign: DyadicSign::Neg,
exponent: -113,
mantissa: 0xa83df971_736c4e6c_1a35479b_ad6d9172_u128,
},
DyadicFloat128 {
sign: DyadicSign::Pos,
exponent: -109,
mantissa: 0x9baa2015_9c5ca461_0aff0b62_54a70fdb_u128,
},
DyadicFloat128 {
sign: DyadicSign::Neg,
exponent: -106,
mantissa: 0xc70af95d_f95d14ad_1094ea1b_e46b2d2f_u128,
},
DyadicFloat128 {
sign: DyadicSign::Pos,
exponent: -103,
mantissa: 0xa838fb48_e79fb706_642da604_6a73b4f8_u128,
},
DyadicFloat128 {
sign: DyadicSign::Neg,
exponent: -101,
mantissa: 0x8fe29f37_02b1e876_39e88664_1c8b3b5d_u128,
},
DyadicFloat128 {
sign: DyadicSign::Neg,
exponent: -100,
mantissa: 0xc8e9a474_0a03f93a_16d2e7a9_627eba4e_u128,
},
DyadicFloat128 {
sign: DyadicSign::Pos,
exponent: -95,
mantissa: 0x8807d1f6_6d646a08_8c7e8900_12d6a5ed_u128,
},
DyadicFloat128 {
sign: DyadicSign::Neg,
exponent: -93,
mantissa: 0xe5c25026_97518024_36878256_fd81c08f_u128,
},
DyadicFloat128 {
sign: DyadicSign::Pos,
exponent: -91,
mantissa: 0xeaa075f0_f5151bed_95ec612f_ab9834a7_u128,
},
DyadicFloat128 {
sign: DyadicSign::Neg,
exponent: -89,
mantissa: 0x9b267222_82d5c666_348d7d1d_0fedfba4_u128,
},
DyadicFloat128 {
sign: DyadicSign::Pos,
exponent: -88,
mantissa: 0x81b45c4c_3e828396_1d5bdede_869c3b84_u128,
},
DyadicFloat128 {
sign: DyadicSign::Neg,
exponent: -89,
mantissa: 0xf4495d43_4bc8dba6_42bdb5d6_c8ba2c9c_u128,
},
DyadicFloat128 {
sign: DyadicSign::Pos,
exponent: -90,
mantissa: 0xc9b29546_0c226270_bb428035_587b6d6a_u128,
},
];
static Q: [DyadicFloat128; 16] = [
DyadicFloat128 {
sign: DyadicSign::Pos,
exponent: -127,
mantissa: 0x80000000_00000000_00000000_00000000_u128,
},
DyadicFloat128 {
sign: DyadicSign::Neg,
exponent: -121,
mantissa: 0x89e18bae_ca9629a1_26927ba2_fbdd66ab_u128,
},
DyadicFloat128 {
sign: DyadicSign::Pos,
exponent: -116,
mantissa: 0x92a90fc2_e905f634_4946e8a0_dd8e3874_u128,
},
DyadicFloat128 {
sign: DyadicSign::Neg,
exponent: -112,
mantissa: 0xc1742696_d29e3846_3e183737_29db8b68_u128,
},
DyadicFloat128 {
sign: DyadicSign::Pos,
exponent: -108,
mantissa: 0xabf61cc0_236a0e90_2572113d_fa339591_u128,
},
DyadicFloat128 {
sign: DyadicSign::Neg,
exponent: -105,
mantissa: 0xcff0fe90_dac1b08e_9a5740ae_b2984fc1_u128,
},
DyadicFloat128 {
sign: DyadicSign::Pos,
exponent: -102,
mantissa: 0x9ff36729_e407c538_cfcea3a7_63f39043_u128,
},
DyadicFloat128 {
sign: DyadicSign::Neg,
exponent: -101,
mantissa: 0xc86ff6a3_9b803a31_d385e9ea_83f9d751_u128,
},
DyadicFloat128 {
sign: DyadicSign::Neg,
exponent: -98,
mantissa: 0xb4a125b1_6cab70f3_0f314558_708843df_u128,
},
DyadicFloat128 {
sign: DyadicSign::Pos,
exponent: -94,
mantissa: 0x9670fd33_f83bcaa7_85cf2d82_c0bf8cd5_u128,
},
DyadicFloat128 {
sign: DyadicSign::Neg,
exponent: -92,
mantissa: 0xd70b4ea5_32fedb9d_78a3c047_05e650f4_u128,
},
DyadicFloat128 {
sign: DyadicSign::Pos,
exponent: -90,
mantissa: 0xb9c7904c_3f97b633_c2c0ad9b_ad573ede_u128,
},
DyadicFloat128 {
sign: DyadicSign::Neg,
exponent: -89,
mantissa: 0xc2023c21_5155e9fe_6fb17bb2_c865becd_u128,
},
DyadicFloat128 {
sign: DyadicSign::Pos,
exponent: -89,
mantissa: 0xd9400a5e_27c58803_22948cf3_6154ac49_u128,
},
DyadicFloat128 {
sign: DyadicSign::Neg,
exponent: -90,
mantissa: 0x87aa272d_6a9700b4_449a9db8_1a93b0ee_u128,
},
DyadicFloat128 {
sign: DyadicSign::Neg,
exponent: -93,
mantissa: 0xd1a86655_5b259611_dfc7affc_6ffb0e20_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);
let f_exp = rational128_exp(x);
(z * r_sqrt * f_exp).fast_as_f64()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_i2() {
assert_eq!(f_i2(7.750000000757874), 257.0034362785801);
assert_eq!(f_i2(7.482812501363189), 198.26765887136534);
assert_eq!(f_i2(-7.750000000757874), 257.0034362785801);
assert_eq!(f_i2(-7.482812501363189), 198.26765887136534);
assert!(f_i2(f64::NAN).is_nan());
assert_eq!(f_i2(f64::INFINITY), f64::INFINITY);
assert_eq!(f_i2(f64::NEG_INFINITY), f64::INFINITY);
assert_eq!(f_i2(0.01), 1.2500104166992188e-5);
assert_eq!(f_i2(-0.01), 1.2500104166992188e-5);
assert_eq!(f_i2(-9.01), 872.9250699638584);
assert_eq!(f_i2(9.01), 872.9250699638584);
}
}