1use crate::common::f_fmla;
30use crate::double_double::DoubleDouble;
31use crate::polyeval::f_estrin_polyeval5;
32use crate::sin::{range_reduction_small, sincos_eval};
33use crate::sin_helper::sincos_eval_dd;
34use crate::sin_table::SIN_K_PI_OVER_128;
35use crate::sincos_reduce::LargeArgumentReduction;
36
37#[cold]
38#[inline(never)]
39fn sinmx_accurate(y: DoubleDouble, sin_k: DoubleDouble, cos_k: DoubleDouble, x: f64) -> f64 {
40 let r_sincos = sincos_eval_dd(y);
41
42 let sin_k_cos_y = DoubleDouble::quick_mult(r_sincos.v_cos, sin_k);
47 let cos_k_sin_y = DoubleDouble::quick_mult(r_sincos.v_sin, cos_k);
48
49 let mut rr = DoubleDouble::full_dd_add(sin_k_cos_y, cos_k_sin_y);
50 rr = DoubleDouble::full_add_f64(rr, -x);
51 rr.to_f64()
52}
53
54#[cold]
55fn sinmx_near_zero_hard(x: f64) -> f64 {
56 const C: [(u64, u64); 8] = [
57 (0xb37137ef120d4bbd, 0xb6db8d4e2aa9f813),
58 (0xbc6555555554e720, 0xbfc5555555555555),
59 (0x3c01110fff8e3ea0, 0x3f81111111111111),
60 (0xbb6314569388b856, 0xbf2a01a01a01a01a),
61 (0xbb61f946e615f3cd, 0x3ec71de3a556c723),
62 (0x3a8998bc94bd3bf0, 0xbe5ae64567f2d4df),
63 (0xba702e73490290eb, 0x3de61245e54b6747),
64 (0xba0182df5b1ffd4c, 0xbd6ae4894bb27213),
65 ];
66 let x2 = DoubleDouble::from_exact_mult(x, x);
67 let mut p = DoubleDouble::mul_add(
68 x2,
69 DoubleDouble::from_bit_pair(C[7]),
70 DoubleDouble::from_bit_pair(C[6]),
71 );
72 p = DoubleDouble::mul_add(x2, p, DoubleDouble::from_bit_pair(C[5]));
73 p = DoubleDouble::mul_add(x2, p, DoubleDouble::from_bit_pair(C[4]));
74 p = DoubleDouble::mul_add(x2, p, DoubleDouble::from_bit_pair(C[3]));
75 p = DoubleDouble::mul_add(x2, p, DoubleDouble::from_bit_pair(C[2]));
76 p = DoubleDouble::mul_add(x2, p, DoubleDouble::from_bit_pair(C[1]));
77 p = DoubleDouble::mul_add(x2, p, DoubleDouble::from_bit_pair(C[0]));
78 p = DoubleDouble::quick_mult_f64(p, x);
79 p.to_f64()
80}
81
82pub fn f_sinmx(x: f64) -> f64 {
86 let x_e = (x.to_bits() >> 52) & 0x7ff;
87 const E_BIAS: u64 = (1u64 << (11 - 1u64)) - 1u64;
88
89 let y: DoubleDouble;
90 let k;
91
92 let mut argument_reduction = LargeArgumentReduction::default();
93
94 if x_e < E_BIAS + 16 {
96 if x_e < E_BIAS - 6 {
97 if x_e < E_BIAS - 32 {
99 if x == 0.0 {
102 return x;
103 }
104
105 let x2 = x * x;
107 let c = f_fmla(
108 x2,
109 f64::from_bits(0x3f81111111111111),
110 f64::from_bits(0xbfc5555555555555),
111 ) * x2;
112 return c * x;
113 }
114
115 let x2 = DoubleDouble::from_exact_mult(x, x);
120 let p = f_estrin_polyeval5(
121 x2.hi,
122 f64::from_bits(0x3f81111111111111),
123 f64::from_bits(0xbf2a01a01a019d2f),
124 f64::from_bits(0x3ec71de3a5269512),
125 f64::from_bits(0xbe5ae642b76ba0f5),
126 f64::from_bits(0x3de6035da3c7eaed),
127 );
128 let mut c = DoubleDouble::mul_f64_add(
129 x2,
130 p,
131 DoubleDouble::from_bit_pair((0xbc655542976eb2af, 0xbfc5555555555555)),
132 );
133 c = DoubleDouble::mul_add(
134 x2,
135 c,
136 DoubleDouble::from_bit_pair((0x34b215c35dc9e9be, 0xb832bde584573661)),
137 );
138 c = DoubleDouble::quick_mult_f64(c, x);
139 let err = f_fmla(
140 x2.hi,
141 f64::from_bits(0x3cc0000000000000), f64::from_bits(0x3bc0000000000000), );
144 let ub = c.hi + (c.lo + err);
145 let lb = c.hi + (c.lo - err);
146 if ub == lb {
147 return c.to_f64();
148 }
149 return sinmx_near_zero_hard(x);
150 }
151 (y, k) = range_reduction_small(x);
153 } else {
154 if x_e > 2 * E_BIAS {
156 return x + f64::NAN;
158 }
159
160 (k, y) = argument_reduction.reduce(x);
162 }
163
164 let r_sincos = sincos_eval(y);
165
166 let sk = SIN_K_PI_OVER_128[(k & 255) as usize];
168 let ck = SIN_K_PI_OVER_128[((k.wrapping_add(64)) & 255) as usize];
169
170 let sin_k = DoubleDouble::from_bit_pair(sk);
171 let cos_k = DoubleDouble::from_bit_pair(ck);
172
173 let sin_k_cos_y = DoubleDouble::quick_mult(r_sincos.v_cos, sin_k);
174 let cos_k_sin_y = DoubleDouble::quick_mult(r_sincos.v_sin, cos_k);
175
176 let mut rr = DoubleDouble::from_exact_add(sin_k_cos_y.hi, cos_k_sin_y.hi);
178 rr.lo += sin_k_cos_y.lo + cos_k_sin_y.lo;
179
180 rr = DoubleDouble::from_exact_add(rr.hi, rr.lo);
181 rr = DoubleDouble::full_add_f64(rr, -x);
182
183 let rlp = rr.lo + r_sincos.err;
184 let rlm = rr.lo - r_sincos.err;
185
186 let r_upper = rr.hi + rlp; let r_lower = rr.hi + rlm; if r_upper == r_lower {
191 return rr.to_f64();
192 }
193
194 sinmx_accurate(y, sin_k, cos_k, x)
195}
196
197#[cfg(test)]
198mod tests {
199 use super::*;
200
201 #[test]
202 fn f_sinf_test() {
203 assert_eq!(f_sinmx(0.0), 0.0);
204 assert_eq!(f_sinmx(1.0), -0.1585290151921035);
205 assert_eq!(f_sinmx(0.3), -0.0044797933386604245);
206 assert_eq!(f_sinmx(-1.0), 0.1585290151921035);
207 assert_eq!(f_sinmx(-0.3), 0.0044797933386604245);
208 assert_eq!(f_sinmx(std::f64::consts::PI / 2.), -0.5707963267948966);
209 assert!(f_sinmx(f64::INFINITY).is_nan());
210 assert!(f_sinmx(f64::NEG_INFINITY).is_nan());
211 }
212}