use crate::common::f_fmla;
use crate::double_double::DoubleDouble;
use crate::err::rerf_poly::RERF_HARD;
use crate::polyeval::f_polyeval7;
#[cold]
#[inline(never)]
fn rerf_poly_tiny_hard(x: f64, z2: DoubleDouble) -> f64 {
const C: [(u64, u64); 10] = [
(0xbc8618f13eb7ca89, 0x3fec5bf891b4ef6b),
(0xbc6d7696fe4a7cd0, 0x3fd2e7fb0bcdf4f2),
(0xbc0cb8b926064434, 0x3f842aa561ecc102),
(0x3c1cd94c2f3e6f09, 0xbf75207c7ef80727),
(0xbbb35c4effe3c87c, 0x3f2db4a8d7c32472),
(0x3bbf1d1edd1e109a, 0x3f20faa7a99a4d3d),
(0xbb9e05d21f4e1755, 0xbef3adb84631c39c),
(0x3b6ee5dc31565280, 0xbec366647cacdcc9),
(0x3b3698f8162c5fac, 0x3eaabb9db9f3b048),
(0xbb026f5401fce891, 0xbe66cd40349520b6),
];
let mut p = DoubleDouble::mul_add(
z2,
DoubleDouble::from_bit_pair(C[9]),
DoubleDouble::from_bit_pair(C[8]),
);
p = DoubleDouble::mul_add(z2, p, DoubleDouble::from_bit_pair(C[7]));
p = DoubleDouble::mul_add(z2, p, DoubleDouble::from_bit_pair(C[6]));
p = DoubleDouble::mul_add(z2, p, DoubleDouble::from_bit_pair(C[5]));
p = DoubleDouble::mul_add(z2, p, DoubleDouble::from_bit_pair(C[4]));
p = DoubleDouble::mul_add(z2, p, DoubleDouble::from_bit_pair(C[3]));
p = DoubleDouble::mul_add(z2, p, DoubleDouble::from_bit_pair(C[2]));
p = DoubleDouble::mul_add(z2, p, DoubleDouble::from_bit_pair(C[1]));
p = DoubleDouble::mul_add(z2, p, DoubleDouble::from_bit_pair(C[0]));
p = DoubleDouble::from_exact_add(p.hi, p.lo);
let z = DoubleDouble::div_dd_f64(p, x);
z.to_f64()
}
#[inline]
fn rerf_poly_tiny(z: f64, x: f64) -> f64 {
let z2 = DoubleDouble::from_exact_mult(z, z);
let p = f_polyeval7(
z2.hi,
f64::from_bits(0xbf75207c7ef80727),
f64::from_bits(0x3f2db4a8d7c36a03),
f64::from_bits(0x3f20faa7a8db7f27),
f64::from_bits(0xbef3adae94983bb2),
f64::from_bits(0xbec3b05fe5c49f32),
f64::from_bits(0x3ed67902690892be),
f64::from_bits(0xbf3090033375e5ee),
);
let mut r = DoubleDouble::quick_mul_f64_add(
z2,
p,
DoubleDouble::from_bit_pair((0xbc0cb29fd910c494, 0x3f842aa561ecc102)),
);
r = DoubleDouble::quick_mul_add(
z2,
r,
DoubleDouble::from_bit_pair((0xbc6d7696ff4f712a, 0x3fd2e7fb0bcdf4f2)),
);
r = DoubleDouble::quick_mul_add(
z2,
r,
DoubleDouble::from_bit_pair((0xbc8618f13eb7ca11, 0x3fec5bf891b4ef6b)),
);
r = DoubleDouble::from_exact_add(r.hi, r.lo);
r = DoubleDouble::div_dd_f64(r, x);
let err = f_fmla(
r.hi,
f64::from_bits(0x3c10000000000000), f64::from_bits(0x3b90000000000000), );
let ub = r.hi + (r.lo + err);
let lb = r.hi + (r.lo - err);
if ub == lb {
return r.to_f64();
}
rerf_poly_tiny_hard(x, z2)
}
#[inline]
fn rerf_poly_hard(x: f64, z2: DoubleDouble, idx: usize) -> f64 {
let c = &RERF_HARD[idx];
let mut p = DoubleDouble::mul_add(
z2,
DoubleDouble::from_bit_pair(c[10]),
DoubleDouble::from_bit_pair(c[9]),
);
p = DoubleDouble::mul_add(z2, p, DoubleDouble::from_bit_pair(c[8]));
p = DoubleDouble::mul_add(z2, p, DoubleDouble::from_bit_pair(c[7]));
p = DoubleDouble::mul_add(z2, p, DoubleDouble::from_bit_pair(c[6]));
p = DoubleDouble::mul_add(z2, p, DoubleDouble::from_bit_pair(c[5]));
p = DoubleDouble::mul_add(z2, p, DoubleDouble::from_bit_pair(c[4]));
p = DoubleDouble::mul_add(z2, p, DoubleDouble::from_bit_pair(c[3]));
p = DoubleDouble::mul_add(z2, p, DoubleDouble::from_bit_pair(c[2]));
p = DoubleDouble::mul_add(z2, p, DoubleDouble::from_bit_pair(c[1]));
p = DoubleDouble::mul_add(z2, p, DoubleDouble::from_bit_pair(c[0]));
p = DoubleDouble::from_exact_add(p.hi, p.lo);
let z = DoubleDouble::div_dd_f64(p, x);
z.to_f64()
}
pub fn f_rerf(x: f64) -> f64 {
let z = f64::from_bits(x.to_bits() & 0x7fff_ffff_ffff_ffff);
let t = z.to_bits();
let ux = t;
if ux > 0x4017afb48dc96626
{
let os = f64::copysign(1.0, x);
const MASK: u64 = 0x7ff0000000000000u64;
if ux > MASK {
return x + x;
}
if ux == MASK {
return os;
}
return f_fmla(-f64::from_bits(0x3c90000000000000), os, os);
}
if z < f64::from_bits(0x3c20000000000000) {
if x == 0. {
return if x.is_sign_negative() {
f64::NEG_INFINITY
} else {
f64::INFINITY
};
}
if z.to_bits() <= 0x38b7f12369dedu64 {
return if x.is_sign_negative() {
f64::NEG_INFINITY
} else {
f64::INFINITY
};
}
const SQRT_PI_OVER_2: DoubleDouble = DoubleDouble::new(
f64::from_bits(0xbc8618f13eb7ca89),
f64::from_bits(0x3fec5bf891b4ef6b),
);
let sx = x * f64::from_bits(0x4690000000000000);
let mut prod = DoubleDouble::div_dd_f64(SQRT_PI_OVER_2, sx);
prod = DoubleDouble::quick_mult_f64(prod, f64::from_bits(0x4690000000000000));
return prod.to_f64();
}
if z.to_bits() < 0x3fb0000000000000u64 {
return rerf_poly_tiny(z, x);
}
const SIXTEEN: u64 = 4 << 52;
let idx =
unsafe { f64::from_bits(z.to_bits().wrapping_add(SIXTEEN)).to_int_unchecked::<usize>() };
let z2 = DoubleDouble::from_exact_mult(z, z);
rerf_poly_hard(x, z2, idx)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_erf() {
assert_eq!(f_rerf(65.), 1.0);
assert_eq!(f_rerf(3.), 1.0000220909849995);
assert_eq!(f_rerf(-3.), -1.0000220909849995);
assert_eq!(f_rerf(-0.03723630312089732), -23.811078627277197);
assert_eq!(
f_rerf(0.0000000000000000002336808689942018),
3.7924667486354975e18
);
assert_eq!(f_rerf(2.000225067138672), 1.004695025872889);
assert_eq!(f_rerf(0.), f64::INFINITY);
assert_eq!(f_rerf(-0.), f64::NEG_INFINITY);
assert!(f_rerf(f64::NAN).is_nan());
}
}