use crate::double_double::DoubleDouble;
use crate::pow_exec::exp_dd_fast;
#[inline]
fn core_erfcx(x: f64) -> DoubleDouble {
if x <= 8. {
const P: [(u64, u64); 12] = [
(0xbc836faeb9a312bb, 0x3ff000000000ee8e),
(0x3c91842f891bec6a, 0x4002ca20a78aaf8f),
(0x3c7916e8a1c30681, 0x4005e955f70aed5b),
(0x3cabad150d828d82, 0x4000646f5807ad07),
(0xbc6f482680d66d9c, 0x3ff1449e03ed381c),
(0xbc7188796156ae19, 0x3fdaa7e997e3b034),
(0xbc5c8af0642761e3, 0x3fbe836282058d4a),
(0xbc372829be2d072f, 0x3f99a2b2adc2ec05),
(0x3c020cc8b96000ab, 0x3f6e6cc3d120a955),
(0x3bdd138e6c136806, 0x3f3743d6735eaf13),
(0xbb9fbd22f0675122, 0x3ef1c1d36ebe29a2),
(0xb89093cc981c934c, 0xbc43c18bc6385c74),
];
let x2 = DoubleDouble::from_exact_mult(x, x);
let x4 = x2 * x2;
let x8 = x4 * x4;
let e0 = DoubleDouble::mul_f64_add(
DoubleDouble::from_bit_pair(P[1]),
x,
DoubleDouble::from_bit_pair(P[0]),
);
let e1 = DoubleDouble::mul_f64_add(
DoubleDouble::from_bit_pair(P[3]),
x,
DoubleDouble::from_bit_pair(P[2]),
);
let e2 = DoubleDouble::mul_f64_add(
DoubleDouble::from_bit_pair(P[5]),
x,
DoubleDouble::from_bit_pair(P[4]),
);
let e3 = DoubleDouble::mul_f64_add(
DoubleDouble::from_bit_pair(P[7]),
x,
DoubleDouble::from_bit_pair(P[6]),
);
let e4 = DoubleDouble::mul_f64_add(
DoubleDouble::from_bit_pair(P[9]),
x,
DoubleDouble::from_bit_pair(P[8]),
);
let e5 = DoubleDouble::mul_f64_add(
DoubleDouble::from_bit_pair(P[11]),
x,
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),
(0xbc95d65be031374e, 0x400bd10c4fb1dbe5),
(0x3cb2d8f661db08a0, 0x4016a649ff973199),
(0x3ca32cbcfdc0ea93, 0x4016daab399c1ffc),
(0xbca2982868536578, 0x400fd61ab892d14c),
(0xbca2e29199e17fd9, 0x40001f56c4d495a3),
(0x3c412ce623a1790a, 0x3fe852b582135164),
(0x3c61152eaf4b0dc5, 0x3fcb760564da7cde),
(0xbc1b57ff91d81959, 0x3fa6e146988df835),
(0x3c17183d8445f19a, 0x3f7b06599b5e912f),
(0xbbd0ada61b85ff98, 0x3f449e39467b73d0),
(0xbb658d84fc735e67, 0x3eff794442532b51),
];
let e0 = DoubleDouble::mul_f64_add_f64(
DoubleDouble::from_bit_pair(Q[1]),
x,
f64::from_bits(0x3ff0000000000000),
);
let e1 = DoubleDouble::mul_f64_add(
DoubleDouble::from_bit_pair(Q[3]),
x,
DoubleDouble::from_bit_pair(Q[2]),
);
let e2 = DoubleDouble::mul_f64_add(
DoubleDouble::from_bit_pair(Q[5]),
x,
DoubleDouble::from_bit_pair(Q[4]),
);
let e3 = DoubleDouble::mul_f64_add(
DoubleDouble::from_bit_pair(Q[7]),
x,
DoubleDouble::from_bit_pair(Q[6]),
);
let e4 = DoubleDouble::mul_f64_add(
DoubleDouble::from_bit_pair(Q[9]),
x,
DoubleDouble::from_bit_pair(Q[8]),
);
let e5 = DoubleDouble::mul_f64_add(
DoubleDouble::from_bit_pair(Q[11]),
x,
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);
return DoubleDouble::div(p_num, p_den);
}
const ONE_OVER_SQRT_PI: DoubleDouble =
DoubleDouble::from_bit_pair((0x3c61ae3a914fed80, 0x3fe20dd750429b6d));
let r = DoubleDouble::from_quick_recip(x);
const P: [(u64, u64); 9] = [
(0xbb1d2ee37e46a4cd, 0x3ff0000000000000),
(0x3ca2e575a4ce3d30, 0x4001303ab00c8bac),
(0xbccf38381e5ee521, 0x4030a97aeed54c9f),
(0xbcc3a2842df0dd3d, 0x4036f7733c9fd2f9),
(0xbcfeaf46506f16ed, 0x4051c5f382750553),
(0x3ccbb9f5e11d176a, 0x404ac0081e0749e0),
(0xbcf374f8966ae2a5, 0x4052082526d99a5c),
(0x3cbb5530b924f224, 0x402feabbf6571c29),
(0xbcbcdd50a3ca4776, 0x40118726e1f2d204),
];
const Q: [(u64, u64); 9] = [
(0x0000000000000000, 0x3ff0000000000000),
(0x3ca2e4613c9e0017, 0x4001303ab00c8bac),
(0xbcce5f17cf14e51d, 0x4031297aeed54c9f),
(0xbcdf7e0fed176f92, 0x40380a76e7a09bb2),
(0x3cfc57b67a2797af, 0x4053bb22e04faf3e),
(0xbcd3e63b7410b46b, 0x404ff46317ae9483),
(0xbce122c15db2653f, 0x405925ef8a428c36),
(0x3ce174ebe3e52c8e, 0x4040f49acfe692e1),
(0xbcda0e267ce6e2e6, 0x40351a07878bfbd3),
];
let mut p_num = DoubleDouble::mul_add(
DoubleDouble::from_bit_pair(P[8]),
r,
DoubleDouble::from_bit_pair(P[7]),
);
p_num = DoubleDouble::mul_add(p_num, r, DoubleDouble::from_bit_pair(P[6]));
p_num = DoubleDouble::mul_add(p_num, r, DoubleDouble::from_bit_pair(P[5]));
p_num = DoubleDouble::mul_add(p_num, r, DoubleDouble::from_bit_pair(P[4]));
p_num = DoubleDouble::mul_add(p_num, r, DoubleDouble::from_bit_pair(P[3]));
p_num = DoubleDouble::mul_add(p_num, r, DoubleDouble::from_bit_pair(P[2]));
p_num = DoubleDouble::mul_add(p_num, r, DoubleDouble::from_bit_pair(P[1]));
p_num = DoubleDouble::mul_add(p_num, r, DoubleDouble::from_bit_pair(P[0]));
let mut p_den = DoubleDouble::mul_add(
DoubleDouble::from_bit_pair(Q[8]),
r,
DoubleDouble::from_bit_pair(Q[7]),
);
p_den = DoubleDouble::mul_add(p_den, r, DoubleDouble::from_bit_pair(Q[6]));
p_den = DoubleDouble::mul_add(p_den, r, DoubleDouble::from_bit_pair(Q[5]));
p_den = DoubleDouble::mul_add(p_den, r, DoubleDouble::from_bit_pair(Q[4]));
p_den = DoubleDouble::mul_add(p_den, r, DoubleDouble::from_bit_pair(Q[3]));
p_den = DoubleDouble::mul_add(p_den, r, DoubleDouble::from_bit_pair(Q[2]));
p_den = DoubleDouble::mul_add(p_den, r, DoubleDouble::from_bit_pair(Q[1]));
p_den = DoubleDouble::mul_add_f64(p_den, r, f64::from_bits(0x3ff0000000000000));
let v0 = DoubleDouble::quick_mult(ONE_OVER_SQRT_PI, r);
let v1 = DoubleDouble::div(p_num, p_den);
DoubleDouble::quick_mult(v0, v1)
}
pub fn f_erfcx(x: f64) -> f64 {
let ux = x.to_bits().wrapping_shl(1);
if ux >= 0x7ffu64 << 53 || ux <= 0x7960000000000000u64 {
if x.is_nan() {
return f64::NAN;
}
if x.to_bits().wrapping_shl(1) == 0 {
return 1.;
}
if x.is_infinite() {
return if x.is_sign_positive() {
0.
} else {
f64::INFINITY
};
}
if ux <= 0x7888f5c28f5c28f6u64 {
return 1.;
}
use crate::common::f_fmla;
const M_TWO_OVER_SQRT_PI: DoubleDouble =
DoubleDouble::from_bit_pair((0xbc71ae3a914fed80, 0xbff20dd750429b6d));
return f_fmla(
M_TWO_OVER_SQRT_PI.lo,
x,
f_fmla(M_TWO_OVER_SQRT_PI.hi, x, 1.),
);
}
if x.to_bits() >= 0xc03aa449ebc84dd6 {
return f64::INFINITY;
}
let ax = x.to_bits() & 0x7fff_ffff_ffff_ffffu64;
if ax <= 0x3ff0000000000000u64 {
const P: [(u64, u64); 11] = [
(0xbb488611350b1950, 0x3ff0000000000000),
(0xbc86ae482c7f2342, 0x3ff9c5d39e89602f),
(0x3c6702d70b807254, 0x3ff5a4c406d6468b),
(0x3c7fe41fc43cfed5, 0x3fe708e7f401bd0c),
(0x3c73a4a355172c6d, 0x3fd0d9a0c1a7126c),
(0x3c5f4c372faa270f, 0x3fb154722e30762e),
(0xbc04c0227976379e, 0x3f88ecebb62ce646),
(0xbbdc9ea151b9eb33, 0x3f580ea84143877b),
(0xbb6dae7001a91491, 0x3f1c3c5f95579b0a),
(0x3b6aca5e82c52897, 0x3ecea4db51968d9e),
(0x3a41c4edd175d2af, 0x3dbc0dccea7fc8ed),
];
let x2 = DoubleDouble::from_exact_mult(x, x);
let x4 = x2 * x2;
let x8 = x4 * x4;
let q0 = DoubleDouble::mul_f64_add(
DoubleDouble::from_bit_pair(P[1]),
x,
DoubleDouble::from_bit_pair(P[0]),
);
let q1 = DoubleDouble::mul_f64_add(
DoubleDouble::from_bit_pair(P[3]),
x,
DoubleDouble::from_bit_pair(P[2]),
);
let q2 = DoubleDouble::mul_f64_add(
DoubleDouble::from_bit_pair(P[5]),
x,
DoubleDouble::from_bit_pair(P[4]),
);
let q3 = DoubleDouble::mul_f64_add(
DoubleDouble::from_bit_pair(P[7]),
x,
DoubleDouble::from_bit_pair(P[6]),
);
let q4 = DoubleDouble::mul_f64_add(
DoubleDouble::from_bit_pair(P[9]),
x,
DoubleDouble::from_bit_pair(P[8]),
);
let r0 = DoubleDouble::mul_add(x2, q1, q0);
let r1 = DoubleDouble::mul_add(x2, q3, q2);
let s0 = DoubleDouble::mul_add(x4, r1, r0);
let s1 = DoubleDouble::mul_add(x2, DoubleDouble::from_bit_pair(P[10]), q4);
let p_num = DoubleDouble::mul_add(x8, s1, s0);
const Q: [(u64, u64); 11] = [
(0x0000000000000000, 0x3ff0000000000000),
(0xbc7bae414cad99c8, 0x4005e9d57765fdce),
(0x3c8fa553bed15758, 0x400b8c670b3fbcda),
(0x3ca6c7ad610f1019, 0x4004f2ca59958153),
(0x3c87787f336cc4e6, 0x3ff55c267090315a),
(0xbc6ef55d4b2c4150, 0x3fde8b84b64b6f4e),
(0x3c570d63c94be3a3, 0x3fbf0d5e36017482),
(0x3c1882a745ef572e, 0x3f962f73633506c1),
(0xbc0850bb6fc82764, 0x3f65593e0dc46acd),
(0xbbb9dc0097d7d776, 0x3f290545603e2f94),
(0xbb776e5781e3889d, 0x3edb29c49d18cf89),
];
let q0 = DoubleDouble::mul_f64_add_f64(
DoubleDouble::from_bit_pair(Q[1]),
x,
f64::from_bits(0x3ff0000000000000),
);
let q1 = DoubleDouble::mul_f64_add(
DoubleDouble::from_bit_pair(Q[3]),
x,
DoubleDouble::from_bit_pair(Q[2]),
);
let q2 = DoubleDouble::mul_f64_add(
DoubleDouble::from_bit_pair(Q[5]),
x,
DoubleDouble::from_bit_pair(Q[4]),
);
let q3 = DoubleDouble::mul_f64_add(
DoubleDouble::from_bit_pair(Q[7]),
x,
DoubleDouble::from_bit_pair(Q[6]),
);
let q4 = DoubleDouble::mul_f64_add(
DoubleDouble::from_bit_pair(Q[9]),
x,
DoubleDouble::from_bit_pair(Q[8]),
);
let r0 = DoubleDouble::mul_add(x2, q1, q0);
let r1 = DoubleDouble::mul_add(x2, q3, q2);
let s0 = DoubleDouble::mul_add(x4, r1, r0);
let s1 = DoubleDouble::mul_add(x2, DoubleDouble::from_bit_pair(Q[10]), q4);
let p_den = DoubleDouble::mul_add(x8, s1, s0);
let v = DoubleDouble::div(p_num, p_den);
return v.to_f64();
}
let mut erfcx_abs_x = core_erfcx(f64::from_bits(ax));
if x < 0. {
erfcx_abs_x = DoubleDouble::from_exact_add(erfcx_abs_x.hi, erfcx_abs_x.lo);
let d2x = DoubleDouble::from_exact_mult(x, x);
let expd2x = exp_dd_fast(d2x);
return DoubleDouble::mul_f64_add(expd2x, 2., -erfcx_abs_x).to_f64();
}
erfcx_abs_x.to_f64()
}
#[cfg(test)]
mod tests {
use crate::f_erfcx;
#[test]
fn test_erfcx() {
assert_eq!(f_erfcx(2.2204460492503131e-18), 1.0);
assert_eq!(f_erfcx(-2.2204460492503131e-18), 1.0);
assert_eq!(f_erfcx(-f64::EPSILON), 1.0000000000000002);
assert_eq!(f_erfcx(f64::EPSILON), 0.9999999999999998);
assert_eq!(f_erfcx(-173.), f64::INFINITY);
assert_eq!(f_erfcx(-9.4324165432), 8.718049147018359e38);
assert_eq!(f_erfcx(9.4324165432), 0.059483265496416374);
assert_eq!(f_erfcx(-1.32432512125), 11.200579112797806);
assert_eq!(f_erfcx(1.32432512125), 0.3528722004785406);
assert_eq!(f_erfcx(-0.532431235), 2.0560589406595384);
assert_eq!(f_erfcx(0.532431235), 0.5994337293294584);
assert_eq!(f_erfcx(1e-26), 1.0);
assert_eq!(f_erfcx(-0.500000000023073), 1.952360489253639);
assert_eq!(f_erfcx(-175.), f64::INFINITY);
assert_eq!(f_erfcx(f64::INFINITY), 0.);
assert_eq!(f_erfcx(f64::NEG_INFINITY), f64::INFINITY);
assert!(f_erfcx(f64::NAN).is_nan());
}
}