use crate::common::f_fmla;
use crate::double_double::DoubleDouble;
use crate::polyeval::f_estrin_polyeval5;
use crate::sin::{range_reduction_small, sincos_eval};
use crate::sin_helper::sincos_eval_dd;
use crate::sin_table::SIN_K_PI_OVER_128;
use crate::sincos_reduce::LargeArgumentReduction;
#[cold]
#[inline(never)]
fn sinmx_accurate(y: DoubleDouble, sin_k: DoubleDouble, cos_k: DoubleDouble, x: f64) -> f64 {
let r_sincos = sincos_eval_dd(y);
let sin_k_cos_y = DoubleDouble::quick_mult(r_sincos.v_cos, sin_k);
let cos_k_sin_y = DoubleDouble::quick_mult(r_sincos.v_sin, cos_k);
let mut rr = DoubleDouble::full_dd_add(sin_k_cos_y, cos_k_sin_y);
rr = DoubleDouble::full_add_f64(rr, -x);
rr.to_f64()
}
#[cold]
fn sinmx_near_zero_hard(x: f64) -> f64 {
const C: [(u64, u64); 8] = [
(0xb37137ef120d4bbd, 0xb6db8d4e2aa9f813),
(0xbc6555555554e720, 0xbfc5555555555555),
(0x3c01110fff8e3ea0, 0x3f81111111111111),
(0xbb6314569388b856, 0xbf2a01a01a01a01a),
(0xbb61f946e615f3cd, 0x3ec71de3a556c723),
(0x3a8998bc94bd3bf0, 0xbe5ae64567f2d4df),
(0xba702e73490290eb, 0x3de61245e54b6747),
(0xba0182df5b1ffd4c, 0xbd6ae4894bb27213),
];
let x2 = DoubleDouble::from_exact_mult(x, x);
let mut p = DoubleDouble::mul_add(
x2,
DoubleDouble::from_bit_pair(C[7]),
DoubleDouble::from_bit_pair(C[6]),
);
p = DoubleDouble::mul_add(x2, p, DoubleDouble::from_bit_pair(C[5]));
p = DoubleDouble::mul_add(x2, p, DoubleDouble::from_bit_pair(C[4]));
p = DoubleDouble::mul_add(x2, p, DoubleDouble::from_bit_pair(C[3]));
p = DoubleDouble::mul_add(x2, p, DoubleDouble::from_bit_pair(C[2]));
p = DoubleDouble::mul_add(x2, p, DoubleDouble::from_bit_pair(C[1]));
p = DoubleDouble::mul_add(x2, p, DoubleDouble::from_bit_pair(C[0]));
p = DoubleDouble::quick_mult_f64(p, x);
p.to_f64()
}
pub fn f_sinmx(x: f64) -> f64 {
let x_e = (x.to_bits() >> 52) & 0x7ff;
const E_BIAS: u64 = (1u64 << (11 - 1u64)) - 1u64;
let y: DoubleDouble;
let k;
let mut argument_reduction = LargeArgumentReduction::default();
if x_e < E_BIAS + 16 {
if x_e < E_BIAS - 6 {
if x_e < E_BIAS - 32 {
if x == 0.0 {
return x;
}
let x2 = x * x;
let c = f_fmla(
x2,
f64::from_bits(0x3f81111111111111),
f64::from_bits(0xbfc5555555555555),
) * x2;
return c * x;
}
let x2 = DoubleDouble::from_exact_mult(x, x);
let p = f_estrin_polyeval5(
x2.hi,
f64::from_bits(0x3f81111111111111),
f64::from_bits(0xbf2a01a01a019d2f),
f64::from_bits(0x3ec71de3a5269512),
f64::from_bits(0xbe5ae642b76ba0f5),
f64::from_bits(0x3de6035da3c7eaed),
);
let mut c = DoubleDouble::mul_f64_add(
x2,
p,
DoubleDouble::from_bit_pair((0xbc655542976eb2af, 0xbfc5555555555555)),
);
c = DoubleDouble::mul_add(
x2,
c,
DoubleDouble::from_bit_pair((0x34b215c35dc9e9be, 0xb832bde584573661)),
);
c = DoubleDouble::quick_mult_f64(c, x);
let err = f_fmla(
x2.hi,
f64::from_bits(0x3cc0000000000000), f64::from_bits(0x3bc0000000000000), );
let ub = c.hi + (c.lo + err);
let lb = c.hi + (c.lo - err);
if ub == lb {
return c.to_f64();
}
return sinmx_near_zero_hard(x);
}
(y, k) = range_reduction_small(x);
} else {
if x_e > 2 * E_BIAS {
return x + f64::NAN;
}
(k, y) = argument_reduction.reduce(x);
}
let r_sincos = sincos_eval(y);
let sk = SIN_K_PI_OVER_128[(k & 255) as usize];
let ck = SIN_K_PI_OVER_128[((k.wrapping_add(64)) & 255) as usize];
let sin_k = DoubleDouble::from_bit_pair(sk);
let cos_k = DoubleDouble::from_bit_pair(ck);
let sin_k_cos_y = DoubleDouble::quick_mult(r_sincos.v_cos, sin_k);
let cos_k_sin_y = DoubleDouble::quick_mult(r_sincos.v_sin, cos_k);
let mut rr = DoubleDouble::from_exact_add(sin_k_cos_y.hi, cos_k_sin_y.hi);
rr.lo += sin_k_cos_y.lo + cos_k_sin_y.lo;
rr = DoubleDouble::from_exact_add(rr.hi, rr.lo);
rr = DoubleDouble::full_add_f64(rr, -x);
let rlp = rr.lo + r_sincos.err;
let rlm = rr.lo - r_sincos.err;
let r_upper = rr.hi + rlp; let r_lower = rr.hi + rlm;
if r_upper == r_lower {
return rr.to_f64();
}
sinmx_accurate(y, sin_k, cos_k, x)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn f_sinf_test() {
assert_eq!(f_sinmx(0.0), 0.0);
assert_eq!(f_sinmx(1.0), -0.1585290151921035);
assert_eq!(f_sinmx(0.3), -0.0044797933386604245);
assert_eq!(f_sinmx(-1.0), 0.1585290151921035);
assert_eq!(f_sinmx(-0.3), 0.0044797933386604245);
assert_eq!(f_sinmx(std::f64::consts::PI / 2.), -0.5707963267948966);
assert!(f_sinmx(f64::INFINITY).is_nan());
assert!(f_sinmx(f64::NEG_INFINITY).is_nan());
}
}