use crate::bits::{EXP_MASK, get_exponent_f64};
use crate::common::f_fmla;
use crate::double_double::DoubleDouble;
use crate::logs::log1p::{LOG_R1_DD, R1};
use crate::logs::log1p_dd;
use crate::polyeval::{f_estrin_polyeval8, f_polyeval4, f_polyeval5};
#[cold]
fn tiny_hard(x: f64) -> f64 {
let x2 = DoubleDouble::from_exact_mult(x, x);
const C: [(u64, u64); 7] = [
(0x3c755555555129de, 0x3fd5555555555555),
(0xbbd333352fe28400, 0xbfd0000000000000),
(0xbc698cb3ef1ea2dd, 0x3fc999999999999a),
(0x3c4269700b3f95d0, 0xbfc55555555546ef),
(0x3c61290e9261823e, 0x3fc2492492491565),
(0x3c6fb0a243c2a59c, 0xbfc000018af8cb7e),
(0x3c3b271ceb5c60a0, 0x3fbc71ca10b30518),
];
let mut r = DoubleDouble::mul_f64_add(
DoubleDouble::from_bit_pair(C[6]),
x,
DoubleDouble::from_bit_pair(C[5]),
);
r = DoubleDouble::mul_f64_add(r, x, DoubleDouble::from_bit_pair(C[4]));
r = DoubleDouble::mul_f64_add(r, x, DoubleDouble::from_bit_pair(C[3]));
r = DoubleDouble::mul_f64_add(r, x, DoubleDouble::from_bit_pair(C[2]));
r = DoubleDouble::mul_f64_add(r, x, DoubleDouble::from_bit_pair(C[1]));
r = DoubleDouble::mul_f64_add(r, x, DoubleDouble::from_bit_pair(C[0]));
r = DoubleDouble::mul_f64_add_f64(r, x, f64::from_bits(0xbfe0000000000000));
r = DoubleDouble::quick_mult(r, x2);
r.to_f64()
}
fn log1pmx_big(x: f64) -> f64 {
let mut x_u = x.to_bits();
let mut x_dd = DoubleDouble::default();
let x_exp: u16 = ((x_u >> 52) & 0x7ff) as u16;
const EXP_BIAS: u16 = (1u16 << (11 - 1u16)) - 1u16;
if x_exp >= EXP_BIAS {
if x_u >= 0x4650_0000_0000_0000u64 {
x_dd.hi = x;
} else {
x_dd = DoubleDouble::from_exact_add(x, 1.0);
}
} else {
x_dd = DoubleDouble::from_exact_add(1.0, x);
}
let xhi_bits = x_dd.hi.to_bits();
let xhi_frac = xhi_bits & ((1u64 << 52) - 1);
x_u = xhi_bits;
let idx: i32 = ((xhi_frac.wrapping_add(1u64 << (52 - 8))) >> (52 - 7)) as i32;
let x_e = (get_exponent_f64(f64::from_bits(xhi_bits)) as i32).wrapping_add(idx >> 7);
let e_x = x_e as f64;
const LOG_2: DoubleDouble = DoubleDouble::new(
f64::from_bits(0x3d2ef35793c76730),
f64::from_bits(0x3fe62e42fefa3800),
);
let r_dd = LOG_R1_DD[idx as usize];
let hi = f_fmla(e_x, LOG_2.hi, f64::from_bits(r_dd.1));
let lo = f_fmla(e_x, LOG_2.lo, f64::from_bits(r_dd.0));
let s_u: i64 = (x_u & EXP_MASK) as i64 - (EXP_BIAS as i64).wrapping_shl(52);
let m_hi = 1f64.to_bits() | xhi_frac;
let m_lo = if x_dd.lo.abs() > x_dd.hi * f64::from_bits(0x3800000000000000) {
(x_dd.lo.to_bits() as i64).wrapping_sub(s_u)
} else {
0
};
let m_dd = DoubleDouble::new(f64::from_bits(m_lo as u64), f64::from_bits(m_hi));
let r = R1[idx as usize];
let v_hi;
let v_lo = DoubleDouble::from_exact_mult(m_dd.lo, f64::from_bits(r));
#[cfg(any(
all(
any(target_arch = "x86", target_arch = "x86_64"),
target_feature = "fma"
),
target_arch = "aarch64"
))]
{
v_hi = f_fmla(f64::from_bits(r), m_dd.hi, -1.0); }
#[cfg(not(any(
all(
any(target_arch = "x86", target_arch = "x86_64"),
target_feature = "fma"
),
target_arch = "aarch64"
)))]
{
use crate::logs::log1p::RCM1;
let c = f64::from_bits(
(idx as u64)
.wrapping_shl(52 - 7)
.wrapping_add(0x3FF0_0000_0000_0000u64),
);
v_hi = f_fmla(
f64::from_bits(r),
m_dd.hi - c,
f64::from_bits(RCM1[idx as usize]),
); }
let mut v_dd = DoubleDouble::from_exact_add(v_hi, v_lo.hi);
v_dd.lo += v_lo.lo;
let mut r1 = DoubleDouble::from_exact_add(hi, v_dd.hi);
const P_COEFFS: [u64; 6] = [
0xbfe0000000000000,
0x3fd5555555555166,
0xbfcfffffffdb7746,
0x3fc99999a8718a60,
0xbfc555874ce8ce22,
0x3fc24335555ddbe5,
];
let v_sq = v_dd.hi * v_dd.hi;
let p0 = f_fmla(
v_dd.hi,
f64::from_bits(P_COEFFS[1]),
f64::from_bits(P_COEFFS[0]),
);
let p1 = f_fmla(
v_dd.hi,
f64::from_bits(P_COEFFS[3]),
f64::from_bits(P_COEFFS[2]),
);
let p2 = f_fmla(
v_dd.hi,
f64::from_bits(P_COEFFS[5]),
f64::from_bits(P_COEFFS[4]),
);
let p = f_polyeval4(v_sq, (v_dd.lo + r1.lo) + lo, p0, p1, p2);
const ERR_HI: [f64; 2] = [f64::from_bits(0x3aa0000000000000), 0.0];
let err_hi = ERR_HI[if hi == 0.0 { 1 } else { 0 }];
let err = f_fmla(v_sq, f64::from_bits(0x3ce0000000000000), err_hi);
r1.lo = p;
r1 = DoubleDouble::from_exact_add(r1.hi, r1.lo);
r1 = DoubleDouble::full_add_f64(r1, -x);
let left = r1.hi + (r1.lo - err);
let right = r1.hi + (r1.lo + err);
if left == right {
return left;
}
log1pmx_accurate_dd(x)
}
#[cold]
fn log1pmx_accurate_dd(x: f64) -> f64 {
let r = log1p_dd(x);
DoubleDouble::full_add_f64(r, -x).to_f64()
}
pub fn f_log1pmx(x: f64) -> f64 {
let ax = x.to_bits() & 0x7fff_ffff_ffff_ffffu64;
let x_e = (x.to_bits() >> 52) & 0x7ff;
if !x.is_normal() {
if x.is_nan() {
return x + x;
}
if x.is_infinite() {
return f64::NAN;
}
if x == 0. {
return x;
}
}
if ax > 0x3f90000000000000u64 {
if x <= -1. {
if x == -1. {
return f64::NEG_INFINITY;
}
return f64::NAN;
}
return log1pmx_big(x);
}
const E_BIAS: u64 = (1u64 << (11 - 1u64)) - 1u64;
if x_e < E_BIAS - 10 {
if x_e < E_BIAS - 100 {
let x2 = x * x;
let dx2 = f_fmla(x, x, -x2);
let rl = dx2 * -0.5;
return f_fmla(x2, -0.5, rl);
}
let p = f_polyeval5(
x,
f64::from_bits(0x3fc999999999999a),
f64::from_bits(0xbfc55555555546ef),
f64::from_bits(0x3fc24924923d3abf),
f64::from_bits(0xbfc000018af7f637),
f64::from_bits(0x3fbc72984db24b6a),
);
let x2 = DoubleDouble::from_exact_mult(x, x);
let mut r = DoubleDouble::f64_mul_f64_add(
x,
p,
DoubleDouble::from_bit_pair((0xbbd3330899095800, 0xbfd0000000000000)),
);
r = DoubleDouble::mul_f64_add(
r,
x,
DoubleDouble::from_bit_pair((0x3c75555538509407, 0x3fd5555555555555)),
);
r = DoubleDouble::mul_f64_add_f64(r, x, f64::from_bits(0xbfe0000000000000));
r = DoubleDouble::quick_mult(r, x2);
const ERR: f64 = f64::from_bits(0x3af0000000000000); let ub = r.hi + (r.lo + ERR);
let lb = r.hi + (r.lo - ERR);
if lb == ub {
return r.to_f64();
}
return tiny_hard(x);
}
let p = f_estrin_polyeval8(
x,
f64::from_bits(0x3fc9999999999997),
f64::from_bits(0xbfc5555555555552),
f64::from_bits(0x3fc249249249fb64),
f64::from_bits(0xbfc000000000f450),
f64::from_bits(0x3fbc71c6e2591149),
f64::from_bits(0xbfb999995cf14d86),
f64::from_bits(0x3fb7494eb6c2c544),
f64::from_bits(0xbfb558bf05690e85),
);
let x2 = DoubleDouble::from_exact_mult(x, x);
let mut r = DoubleDouble::f64_mul_f64_add(
x,
p,
DoubleDouble::from_bit_pair((0xbb9d89dc15a38000, 0xbfd0000000000000)),
);
r = DoubleDouble::mul_f64_add(
r,
x,
DoubleDouble::from_bit_pair((0x3c7555a15a94e505, 0x3fd5555555555555)),
);
r = DoubleDouble::mul_f64_add_f64(r, x, f64::from_bits(0xbfe0000000000000));
r = DoubleDouble::quick_mult(r, x2);
r.to_f64()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_log1pmx() {
assert_eq!(
f_log1pmx(0.00000000000000005076835735015165),
-0.0000000000000000000000000000000012887130540163486
);
assert_eq!(f_log1pmx(5.), -3.208240530771945);
assert_eq!(f_log1pmx(-0.99), -3.6151701859880907);
assert_eq!(f_log1pmx(-1.), f64::NEG_INFINITY);
assert_eq!(
f_log1pmx(1.0000000000008708e-13),
-0.0000000000000000000000000050000000000083744
);
assert_eq!(
f_log1pmx(1.0000000000008708e-26),
-0.00000000000000000000000000000000000000000000000000005000000000008707
);
assert_eq!(f_log1pmx(0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012890176556069385),
-0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000830783258233204);
}
}