use crate::bessel::j0f::j1f_rsqrt;
use crate::common::f_fmla;
use crate::exponents::core_expf;
use crate::polyeval::{
f_estrin_polyeval5, f_estrin_polyeval7, f_estrin_polyeval8, f_estrin_polyeval9, f_polyeval6,
};
pub fn f_i0f(x: f32) -> f32 {
let ux = x.to_bits().wrapping_shl(1);
if ux >= 0xffu32 << 24 || ux == 0 {
if ux == 0 {
return 1.;
}
if x.is_infinite() {
return f32::INFINITY;
}
return x + f32::NAN; }
let xb = x.to_bits() & 0x7fff_ffff;
if xb >= 0x42b7cd32u32 {
return f32::INFINITY;
}
if xb < 0x40f00000u32 {
if xb < 0x3f800000u32 {
if xb <= 0x34000000u32 {
#[cfg(any(
all(
any(target_arch = "x86", target_arch = "x86_64"),
target_feature = "fma"
),
target_arch = "aarch64"
))]
{
use crate::common::f_fmlaf;
return f_fmlaf(x, x * 0.25, 1.);
}
#[cfg(not(any(
all(
any(target_arch = "x86", target_arch = "x86_64"),
target_feature = "fma"
),
target_arch = "aarch64"
)))]
{
let dx = x as f64;
return f_fmla(dx, dx * 0.25, 1.) as f32;
}
}
return i0f_small(f32::from_bits(xb)) as f32;
} else if xb <= 0x40600000u32 {
return i0f_1_to_3p5(f32::from_bits(xb));
} else if xb <= 0x40c00000u32 {
return i0f_3p5_to_6(f32::from_bits(xb));
}
return i0f_6_to_7p5(f32::from_bits(xb));
}
i0f_asympt(f32::from_bits(xb))
}
#[inline]
pub(crate) fn i0f_small(x: f32) -> f64 {
let dx = x as f64;
const C: f64 = 1. / 4.;
let eval_x = dx * dx * C;
let p = f_estrin_polyeval7(
eval_x,
f64::from_bits(0x3ff000000000013a),
f64::from_bits(0x3fcffffffffc20b6),
f64::from_bits(0x3f9c71c71e6cd6a2),
f64::from_bits(0x3f5c71c65b0af15f),
f64::from_bits(0x3f1234796fceb081),
f64::from_bits(0x3ec0280faf31678c),
f64::from_bits(0x3e664fd494223545),
);
f_fmla(p, eval_x, 1.)
}
#[inline]
fn i0f_1_to_3p5(x: f32) -> f32 {
let dx = x as f64;
const C: f64 = 1. / 4.;
let eval_x = dx * dx * C;
let p_num = f_polyeval6(
eval_x,
f64::from_bits(0x3feffffffffffb69),
f64::from_bits(0x3fc9ed7bd9dc97a7),
f64::from_bits(0x3f915c14693c842e),
f64::from_bits(0x3f45c6dc6a719e42),
f64::from_bits(0x3eeacb79eba725f7),
f64::from_bits(0x3e7b51e2acfc4355),
);
let p_den = f_estrin_polyeval5(
eval_x,
f64::from_bits(0x3ff0000000000000),
f64::from_bits(0xbfa84a10988f28eb),
f64::from_bits(0x3f50f5599197a4be),
f64::from_bits(0xbeea420cf9b13b1b),
f64::from_bits(0x3e735d0c1eb6ed7d),
);
f_fmla(p_num / p_den, eval_x, 1.) as f32
}
#[inline]
fn i0f_6_to_7p5(x: f32) -> f32 {
let dx = x as f64;
const C: f64 = 1. / 4.;
let eval_x = dx * dx * C;
let p_num = f_estrin_polyeval8(
eval_x,
f64::from_bits(0x3fefffffffffff7d),
f64::from_bits(0x3fcb373b00569ccf),
f64::from_bits(0x3f939069c3363b81),
f64::from_bits(0x3f4c2095c90c66b3),
f64::from_bits(0x3ef6713f648413db),
f64::from_bits(0x3e947efa2f9936b4),
f64::from_bits(0x3e2486a182f49420),
f64::from_bits(0x3da213034a33de33),
);
let p_den = f_estrin_polyeval7(
eval_x,
f64::from_bits(0x3ff0000000000000),
f64::from_bits(0xbfa32313fea59d9e),
f64::from_bits(0x3f460594c2ec6706),
f64::from_bits(0xbedf725fb714690f),
f64::from_bits(0x3e6d9cb39b19555c),
f64::from_bits(0xbdf1900e3abcb7a6),
f64::from_bits(0x3d64a21a2ea78ef6),
);
f_fmla(p_num / p_den, eval_x, 1.) as f32
}
#[inline]
fn i0f_3p5_to_6(x: f32) -> f32 {
let dx = x as f64;
const C: f64 = 1. / 4.;
let eval_x = dx * dx * C;
let p_num = f_polyeval6(
eval_x,
f64::from_bits(0x3feffffffffd9550),
f64::from_bits(0x3fc97e18ee033fb4),
f64::from_bits(0x3f90b3199079bce1),
f64::from_bits(0x3f442c300a425372),
f64::from_bits(0x3ee7831030ae18ca),
f64::from_bits(0x3e76387d67354932),
);
let p_den = f_polyeval6(
eval_x,
f64::from_bits(0x3ff0000000000000),
f64::from_bits(0xbfaa079c484e406a),
f64::from_bits(0x3f5452098f1556fb),
f64::from_bits(0xbef33efb4a8128ac),
f64::from_bits(0x3e865996e19448ca),
f64::from_bits(0xbe09acbb64533c3e),
);
f_fmla(p_num / p_den, eval_x, 1.) as f32
}
#[inline]
fn i0f_asympt(x: f32) -> f32 {
let dx = x as f64;
let recip = 1. / dx;
let p_num = f_estrin_polyeval9(
recip,
f64::from_bits(0x3fd9884533d44829),
f64::from_bits(0xc02c940f40595581),
f64::from_bits(0x406d41c495c2f762),
f64::from_bits(0xc0a10ab76dda4520),
f64::from_bits(0x40c825b1c2a48d07),
f64::from_bits(0xc0e481d606d0b748),
f64::from_bits(0x40f34759deefbd40),
f64::from_bits(0xc0ef4b7fb49fa116),
f64::from_bits(0x40c409a6f882ba00),
);
let p_den = f_estrin_polyeval9(
recip,
f64::from_bits(0x3ff0000000000000),
f64::from_bits(0xc041f8a9131ad229),
f64::from_bits(0x408278e56af035bb),
f64::from_bits(0xc0b5a34a108f3e35),
f64::from_bits(0x40dee6f278ee24f5),
f64::from_bits(0xc0fa95093b0c4f9f),
f64::from_bits(0x4109982b87f75651),
f64::from_bits(0xc10618cc3c91e2db),
f64::from_bits(0x40e30895aec6fc4f),
);
let z = p_num / p_den;
let e = core_expf(x);
let r_sqrt = j1f_rsqrt(dx);
(z * r_sqrt * e) as f32
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_i0f() {
assert!(f_i0f(f32::NAN).is_nan());
assert_eq!(f_i0f(f32::NEG_INFINITY), f32::INFINITY);
assert_eq!(f_i0f(f32::INFINITY), f32::INFINITY);
assert_eq!(f_i0f(1.), 1.2660658);
assert_eq!(f_i0f(5.), 27.239872);
assert_eq!(f_i0f(16.), 893446.25);
assert_eq!(f_i0f(32.), 5590908000000.0);
assert_eq!(f_i0f(92.0), f32::INFINITY);
assert_eq!(f_i0f(0.), 1.0);
assert_eq!(f_i0f(28.), 109534600000.0);
assert_eq!(f_i0f(-28.), 109534600000.0);
assert_eq!(f_i0f(-16.), 893446.25);
assert_eq!(f_i0f(-32.), 5590908000000.0);
}
}