1use crate::common::{dyad_fmla, f_fmla, min_normal_f64};
30use crate::double_double::DoubleDouble;
31use crate::dyadic_float::{DyadicFloat128, DyadicSign};
32use crate::round::RoundFinite;
33use crate::sin_helper::sincos_eval_dd;
34use crate::sin_table::SIN_K_PI_OVER_128;
35use crate::sincos_dyadic::SIN_K_PI_OVER_128_F128;
36use crate::sincos_reduce::LargeArgumentReduction;
37
38#[inline]
45pub(crate) fn range_reduction_small(x: f64) -> (DoubleDouble, u64) {
46 const MPI_OVER_128: [u64; 3] = [0xbf9921fb54400000, 0xbd70b4611a600000, 0xbb43198a2e037073];
47 const ONE_TWENTY_EIGHT_OVER_PI_D: f64 = f64::from_bits(0x40445f306dc9c883);
48 let prod_hi = x * ONE_TWENTY_EIGHT_OVER_PI_D;
49 let kd = prod_hi.round_finite();
50
51 let y_hi = f_fmla(kd, f64::from_bits(MPI_OVER_128[0]), x); let u_hi = f_fmla(kd, f64::from_bits(MPI_OVER_128[1]), y_hi);
57
58 let u0 = y_hi - u_hi; let u1 = f_fmla(kd, f64::from_bits(MPI_OVER_128[1]), u0); let u_lo = f_fmla(kd, f64::from_bits(MPI_OVER_128[2]), u1);
62 (DoubleDouble::new(u_lo, u_hi), unsafe {
67 kd.to_int_unchecked::<i64>() as u64 })
69}
70
71#[inline]
72pub(crate) fn get_sin_k_rational(kk: u64) -> DyadicFloat128 {
73 let idx = if (kk & 64) != 0 {
74 64 - (kk & 63)
75 } else {
76 kk & 63
77 };
78 let mut ans = SIN_K_PI_OVER_128_F128[idx as usize];
79 if (kk & 128) != 0 {
80 ans.sign = DyadicSign::Neg;
81 }
82 ans
83}
84
85pub(crate) struct SinCos {
86 pub(crate) v_sin: DoubleDouble,
87 pub(crate) v_cos: DoubleDouble,
88 pub(crate) err: f64,
89}
90
91#[inline]
92pub(crate) fn sincos_eval(u: DoubleDouble) -> SinCos {
93 let u_hi_sq = u.hi * u.hi; let p1 = f_fmla(
106 u_hi_sq,
107 f64::from_bits(0xbf2a01a01a01a01a),
108 f64::from_bits(0x3f81111111111111),
109 );
110 let q1 = f_fmla(
112 u_hi_sq,
113 f64::from_bits(0x3fa5555555555555),
114 f64::from_bits(0xbfe0000000000000),
115 );
116 let u_hi_3 = u_hi_sq * u.hi;
117 let p2 = f_fmla(u_hi_sq, p1, f64::from_bits(0xbfc5555555555555));
119 let q2 = f_fmla(u_hi_sq, q1, 1.0);
121 let sin_lo = f_fmla(u_hi_3, p2, u.lo * q2);
122 let u_hi_neg_half = (-0.5) * u.hi;
138
139 let (mut v_lo, v_hi);
140
141 #[cfg(any(
142 all(
143 any(target_arch = "x86", target_arch = "x86_64"),
144 target_feature = "fma"
145 ),
146 all(target_arch = "aarch64", target_feature = "neon")
147 ))]
148 {
149 v_hi = f_fmla(u.hi, u_hi_neg_half, 1.0);
150 v_lo = 1.0 - v_hi; v_lo = f_fmla(u.hi, u_hi_neg_half, v_lo);
152 }
153
154 #[cfg(not(any(
155 all(
156 any(target_arch = "x86", target_arch = "x86_64"),
157 target_feature = "fma"
158 ),
159 all(target_arch = "aarch64", target_feature = "neon")
160 )))]
161 {
162 let u_hi_sq_neg_half = DoubleDouble::from_exact_mult(u.hi, u_hi_neg_half);
163 let v = DoubleDouble::from_exact_add(1.0, u_hi_sq_neg_half.hi);
164 v_lo = v.lo;
165 v_lo += u_hi_sq_neg_half.lo;
166 v_hi = v.hi;
167 }
168
169 let r1 = f_fmla(
171 u_hi_sq,
172 f64::from_bits(0x3efa01a01a01a01a),
173 f64::from_bits(0xbf56c16c16c16c17),
174 );
175 let s1 = f_fmla(u_hi_sq, f64::from_bits(0x3fc5555555555555), -1.0);
177 let u_hi_4 = u_hi_sq * u_hi_sq;
178 let u_hi_u_lo = u.hi * u.lo;
179 let r2 = f_fmla(u_hi_sq, r1, f64::from_bits(0x3fa5555555555555));
181 let s2 = f_fmla(u_hi_u_lo, s1, v_lo);
183 let cos_lo = f_fmla(u_hi_4, r2, s2);
184 let sin_u = DoubleDouble::from_exact_add(u.hi, sin_lo);
187 let cos_u = DoubleDouble::from_exact_add(v_hi, cos_lo);
188
189 let err = f_fmla(
190 u_hi_3,
191 f64::from_bits(0x3cc0000000000000),
192 f64::from_bits(0x3960000000000000),
193 );
194
195 SinCos {
196 v_sin: sin_u,
197 v_cos: cos_u,
198 err,
199 }
200}
201
202#[cold]
203#[inline(never)]
204fn sin_accurate(y: DoubleDouble, sin_k: DoubleDouble, cos_k: DoubleDouble) -> f64 {
205 let r_sincos = sincos_eval_dd(y);
206
207 let sin_k_cos_y = DoubleDouble::quick_mult(r_sincos.v_cos, sin_k);
212 let cos_k_sin_y = DoubleDouble::quick_mult(r_sincos.v_sin, cos_k);
213
214 let rr = DoubleDouble::full_dd_add(sin_k_cos_y, cos_k_sin_y);
215 rr.to_f64()
216}
217
218#[cold]
219pub(crate) fn sin_accurate_near_zero(x: f64) -> f64 {
220 const C: [(u64, u64); 7] = [
225 (0x0000000000000000, 0x3ff0000000000000),
226 (0xbc65555555555217, 0xbfc5555555555555),
227 (0x3c011110f7d49e8c, 0x3f81111111111111),
228 (0xbb65e5b30986fc80, 0xbf2a01a01a01a01a),
229 (0xbb689e86245bd566, 0x3ec71de3a556c6e5),
230 (0xbaccb3d55ccfca58, 0xbe5ae64567954935),
231 (0x3a6333ef7a00ce40, 0x3de6120ca00ce3a2),
232 ];
233 let x2 = DoubleDouble::from_exact_mult(x, x);
234 let mut p = DoubleDouble::quick_mul_add(
235 x2,
236 DoubleDouble::from_bit_pair(C[6]),
237 DoubleDouble::from_bit_pair(C[5]),
238 );
239 p = DoubleDouble::quick_mul_add(x2, p, DoubleDouble::from_bit_pair(C[4]));
240 p = DoubleDouble::quick_mul_add(x2, p, DoubleDouble::from_bit_pair(C[3]));
241 p = DoubleDouble::quick_mul_add(x2, p, DoubleDouble::from_bit_pair(C[2]));
242 p = DoubleDouble::quick_mul_add(x2, p, DoubleDouble::from_bit_pair(C[1]));
243 p = DoubleDouble::quick_mul_add_f64(x2, p, f64::from_bits(0x3ff0000000000000));
244 p = DoubleDouble::quick_mult_f64(p, x);
245 p.to_f64()
246}
247
248pub fn f_sin(x: f64) -> f64 {
252 let x_e = (x.to_bits() >> 52) & 0x7ff;
253 const E_BIAS: u64 = (1u64 << (11 - 1u64)) - 1u64;
254
255 let y: DoubleDouble;
256 let k;
257
258 let mut argument_reduction = LargeArgumentReduction::default();
259
260 if x_e < E_BIAS + 16 {
261 let ax = x.to_bits() & 0x7fff_ffff_ffff_ffff;
263 if ax <= 0x3fa921fbd34a9550 {
264 if x_e < E_BIAS - 26 {
266 if x == 0.0 {
268 return x;
270 }
271
272 return dyad_fmla(x, f64::from_bits(0xbc90000000000000), x);
274 }
275
276 let x2 = x * x;
283 let x4 = x2 * x2;
284 const C: [u64; 4] = [
285 0xbfc5555555555555,
286 0x3f8111111110e45a,
287 0xbf2a019ffd7fdaaf,
288 0x3ec71819b9bf01ef,
289 ];
290 let p01 = f_fmla(x2, f64::from_bits(C[1]), f64::from_bits(C[0]));
291 let p23 = f_fmla(x2, f64::from_bits(C[3]), f64::from_bits(C[2]));
292 let w0 = f_fmla(x4, p23, p01);
293 let w1 = x2 * w0 * x;
294 let r = DoubleDouble::from_exact_add(x, w1);
295 let err = f_fmla(
296 x2,
297 f64::from_bits(0x3cb0000000000000), f64::from_bits(0x3be0000000000000), );
300 let ub = r.hi + (r.lo + err);
301 let lb = r.hi + (r.lo - err);
302 if ub == lb {
303 return ub;
304 }
305 return sin_accurate_near_zero(x);
306 }
307
308 (y, k) = range_reduction_small(x);
310 } else {
311 if x_e > 2 * E_BIAS {
313 return x + f64::NAN;
315 }
316
317 (k, y) = argument_reduction.reduce(x);
319 }
320
321 let r_sincos = sincos_eval(y);
322
323 let sk = SIN_K_PI_OVER_128[(k & 255) as usize];
325 let ck = SIN_K_PI_OVER_128[((k.wrapping_add(64)) & 255) as usize];
326
327 let sin_k = DoubleDouble::from_bit_pair(sk);
328 let cos_k = DoubleDouble::from_bit_pair(ck);
329
330 let sin_k_cos_y = DoubleDouble::quick_mult(r_sincos.v_cos, sin_k);
331 let cos_k_sin_y = DoubleDouble::quick_mult(r_sincos.v_sin, cos_k);
332
333 let mut rr = DoubleDouble::from_exact_add(sin_k_cos_y.hi, cos_k_sin_y.hi);
335 rr.lo += sin_k_cos_y.lo + cos_k_sin_y.lo;
336
337 let rlp = rr.lo + r_sincos.err;
338 let rlm = rr.lo - r_sincos.err;
339
340 let r_upper = rr.hi + rlp; let r_lower = rr.hi + rlm; if r_upper == r_lower {
345 return rr.to_f64();
346 }
347
348 sin_accurate(y, sin_k, cos_k)
349}
350
351#[cold]
352#[inline(never)]
353fn cos_accurate(y: DoubleDouble, msin_k: DoubleDouble, cos_k: DoubleDouble) -> f64 {
354 let r_sincos = sincos_eval_dd(y);
355
356 let sin_k_cos_y = DoubleDouble::quick_mult(r_sincos.v_cos, cos_k);
362 let cos_k_sin_y = DoubleDouble::quick_mult(r_sincos.v_sin, msin_k);
363
364 let rr = DoubleDouble::full_dd_add(sin_k_cos_y, cos_k_sin_y);
365 rr.to_f64()
366}
367
368#[cold]
369pub(crate) fn cos_accurate_near_zero(x: f64) -> f64 {
370 const C: [(u64, u64); 6] = [
375 (0xba2fa1c000000000, 0x3ff0000000000000),
376 (0x3b1cd6ead3800000, 0xbfe0000000000000),
377 (0x3c45112931063916, 0x3fa5555555555555),
378 (0xbba1c1e3ab3b09e0, 0xbf56c16c16c16b2b),
379 (0x3b937bc2f4ea7db6, 0x3efa01a019495fca),
380 (0x3b307fd5e1b021b3, 0xbe927e0d57d894f8),
381 ];
382 let x2 = DoubleDouble::from_exact_mult(x, x);
383 let mut p = DoubleDouble::quick_mul_add(
384 x2,
385 DoubleDouble::from_bit_pair(C[5]),
386 DoubleDouble::from_bit_pair(C[4]),
387 );
388 p = DoubleDouble::quick_mul_add(x2, p, DoubleDouble::from_bit_pair(C[3]));
389 p = DoubleDouble::quick_mul_add(x2, p, DoubleDouble::from_bit_pair(C[2]));
390 p = DoubleDouble::quick_mul_add(x2, p, DoubleDouble::from_bit_pair(C[1]));
391 p = DoubleDouble::quick_mul_add(x2, p, DoubleDouble::from_bit_pair(C[0]));
392 p.to_f64()
393}
394
395pub fn f_cos(x: f64) -> f64 {
399 let x_e = (x.to_bits() >> 52) & 0x7ff;
400 const E_BIAS: u64 = (1u64 << (11 - 1u64)) - 1u64;
401
402 let y: DoubleDouble;
403 let k;
404
405 let mut argument_reduction = LargeArgumentReduction::default();
406
407 if x_e < E_BIAS + 16 {
408 let ax = x.to_bits() & 0x7fff_ffff_ffff_ffff;
410 if ax <= 0x3fa921fbd34a9550 {
411 if x_e < E_BIAS - 27 {
413 if x == 0.0 {
415 return 1.0;
417 }
418 return 1.0 - min_normal_f64();
420 }
421
422 let x2 = x * x;
429 let x4 = x2 * x2;
430 const C: [u64; 4] = [
431 0xbfe0000000000000,
432 0x3fa55555555554a4,
433 0xbf56c16c1619b84a,
434 0x3efa013d3d01cf7f,
435 ];
436 let p01 = f_fmla(x2, f64::from_bits(C[1]), f64::from_bits(C[0]));
437 let p23 = f_fmla(x2, f64::from_bits(C[3]), f64::from_bits(C[2]));
438 let w0 = f_fmla(x4, p23, p01);
439 let w1 = x2 * w0;
440 let r = DoubleDouble::from_exact_add(1., w1);
441 let err = f_fmla(
442 x2,
443 f64::from_bits(0x3cb0000000000000), f64::from_bits(0x3be0000000000000), );
446
447 let ub = r.hi + (r.lo + err);
448 let lb = r.hi + (r.lo - err);
449 if ub == lb {
450 return ub;
451 }
452 return cos_accurate_near_zero(x);
453 } else {
454 (y, k) = range_reduction_small(x);
456 }
457 } else {
458 if x_e > 2 * E_BIAS {
460 return x + f64::NAN;
462 }
463
464 (k, y) = argument_reduction.reduce(x);
466 }
467 let r_sincos = sincos_eval(y);
468
469 let sk = SIN_K_PI_OVER_128[(k.wrapping_add(128) & 255) as usize];
474 let ck = SIN_K_PI_OVER_128[((k.wrapping_add(64)) & 255) as usize];
475 let msin_k = DoubleDouble::from_bit_pair(sk);
476 let cos_k = DoubleDouble::from_bit_pair(ck);
477
478 let cos_k_cos_y = DoubleDouble::quick_mult(r_sincos.v_cos, cos_k);
479 let cos_k_msin_y = DoubleDouble::quick_mult(r_sincos.v_sin, msin_k);
480
481 let mut rr = DoubleDouble::from_exact_add(cos_k_cos_y.hi, cos_k_msin_y.hi);
483 rr.lo += cos_k_cos_y.lo + cos_k_msin_y.lo;
484 let rlp = rr.lo + r_sincos.err;
485 let rlm = rr.lo - r_sincos.err;
486
487 let r_upper = rr.hi + rlp; let r_lower = rr.hi + rlm; if r_upper == r_lower {
492 return rr.to_f64();
493 }
494 cos_accurate(y, msin_k, cos_k)
495}
496
497#[cfg(test)]
498mod tests {
499 use super::*;
500
501 #[test]
502 fn cos_test() {
503 assert_eq!(f_cos(0.0), 1.0);
504 assert_eq!(f_cos(0.0024312312), 0.9999970445588818);
505 assert_eq!(f_cos(1.0), 0.5403023058681398);
506 assert_eq!(f_cos(-0.5), 0.8775825618903728);
507 assert!(f_cos(f64::INFINITY).is_nan());
508 assert!(f_cos(f64::NEG_INFINITY).is_nan());
509 assert!(f_cos(f64::NAN).is_nan());
510 }
511
512 #[test]
513 fn sin_test() {
514 assert_eq!(f_sin(0.00000002149119332890786), 0.00000002149119332890786);
515 assert_eq!(f_sin(2.8477476437362989E-306), 2.8477476437362989E-306);
516 assert_eq!(f_sin(0.0024312312), 0.002431228804879309);
517 assert_eq!(f_sin(0.0), 0.0);
518 assert_eq!(f_sin(1.0), 0.8414709848078965);
519 assert_eq!(f_sin(-0.5), -0.479425538604203);
520 assert!(f_sin(f64::INFINITY).is_nan());
521 assert!(f_sin(f64::NEG_INFINITY).is_nan());
522 assert!(f_sin(f64::NAN).is_nan());
523 }
524}