1use crate::common::*;
30use crate::double_double::DoubleDouble;
31use crate::sin::{
32 cos_accurate_near_zero, range_reduction_small, sin_accurate_near_zero, sincos_eval,
33};
34use crate::sin_helper::sincos_eval_dd;
35use crate::sin_table::SIN_K_PI_OVER_128;
36use crate::sincos_reduce::LargeArgumentReduction;
37use std::hint::black_box;
38
39pub fn f_sincos(x: f64) -> (f64, f64) {
43 let x_e = (x.to_bits() >> 52) & 0x7ff;
44 const E_BIAS: u64 = (1u64 << (11 - 1u64)) - 1u64;
45
46 let y: DoubleDouble;
47 let k;
48
49 let mut argument_reduction = LargeArgumentReduction::default();
50
51 if x_e < E_BIAS + 16 {
53 let ax = x.to_bits() & 0x7fff_ffff_ffff_ffff;
55 if ax <= 0x3fa921fbd34a9550 {
56 if x_e < E_BIAS - 27 {
58 if x == 0.0 {
60 return (x, 1.0);
61 }
62 let s_sin = dyad_fmla(x, f64::from_bits(0xbc90000000000000), x);
64 let s_cos = black_box(1.0) - min_normal_f64();
65 return (s_sin, s_cos);
66 }
67
68 const SIN_C: [u64; 4] = [
74 0xbfc5555555555555,
75 0x3f8111111110e45a,
76 0xbf2a019ffd7fdaaf,
77 0x3ec71819b9bf01ef,
78 ];
79
80 let x2 = x * x;
81 let x4 = x2 * x2;
82
83 let p01 = f_fmla(x2, f64::from_bits(SIN_C[1]), f64::from_bits(SIN_C[0]));
84 let p23 = f_fmla(x2, f64::from_bits(SIN_C[3]), f64::from_bits(SIN_C[2]));
85 let w0 = f_fmla(x4, p23, p01);
86 let w1 = x2 * w0 * x;
87 let r_sin = DoubleDouble::from_exact_add(x, w1);
88
89 const COS_C: [u64; 4] = [
95 0xbfe0000000000000,
96 0x3fa55555555554a4,
97 0xbf56c16c1619b84a,
98 0x3efa013d3d01cf7f,
99 ];
100 let p01 = f_fmla(x2, f64::from_bits(COS_C[1]), f64::from_bits(COS_C[0]));
101 let p23 = f_fmla(x2, f64::from_bits(COS_C[3]), f64::from_bits(COS_C[2]));
102 let w0 = f_fmla(x4, p23, p01);
103 let w1 = x2 * w0;
104 let r_cos = DoubleDouble::from_exact_add(1., w1);
105
106 let err = f_fmla(
107 x2,
108 f64::from_bits(0x3cb0000000000000), f64::from_bits(0x3be0000000000000), );
111
112 let sin_ub = r_sin.hi + (r_sin.lo + err);
113 let sin_lb = r_sin.hi + (r_sin.lo - err);
114 let sin_x = if sin_ub == sin_lb {
115 sin_ub
116 } else {
117 sin_accurate_near_zero(x)
118 };
119
120 let cos_ub = r_cos.hi + (r_cos.lo + err);
121 let cos_lb = r_cos.hi + (r_cos.lo - err);
122 let cos_x = if cos_ub == cos_lb {
123 cos_ub
124 } else {
125 cos_accurate_near_zero(x)
126 };
127 return (sin_x, cos_x);
128 } else {
129 (y, k) = range_reduction_small(x);
131 }
132 } else {
133 if x_e > 2 * E_BIAS {
135 return (x + f64::NAN, x + f64::NAN);
137 }
138
139 (k, y) = argument_reduction.reduce(x);
141 }
142
143 let r_sincos = sincos_eval(y);
144 let (sin_y, cos_y) = (r_sincos.v_sin, r_sincos.v_cos);
145
146 let sk = SIN_K_PI_OVER_128[(k & 255) as usize];
149 let ck = SIN_K_PI_OVER_128[((k.wrapping_add(64)) & 255) as usize];
150 let sin_k = DoubleDouble::from_bit_pair(sk);
151 let cos_k = DoubleDouble::from_bit_pair(ck);
152
153 let msin_k = -sin_k;
154
155 let sin_k_cos_y = DoubleDouble::quick_mult(cos_y, sin_k);
160 let cos_k_sin_y = DoubleDouble::quick_mult(sin_y, cos_k);
161 let cos_k_cos_y = DoubleDouble::quick_mult(cos_y, cos_k);
164 let msin_k_sin_y = DoubleDouble::quick_mult(sin_y, msin_k);
165
166 let mut sin_dd = DoubleDouble::from_exact_add(sin_k_cos_y.hi, cos_k_sin_y.hi);
168 let mut cos_dd = DoubleDouble::from_exact_add(cos_k_cos_y.hi, msin_k_sin_y.hi);
170 sin_dd.lo += sin_k_cos_y.lo + cos_k_sin_y.lo;
171 cos_dd.lo += msin_k_sin_y.lo + cos_k_cos_y.lo;
172
173 let sin_lp = sin_dd.lo + r_sincos.err;
174 let sin_lm = sin_dd.lo - r_sincos.err;
175 let cos_lp = cos_dd.lo + r_sincos.err;
176 let cos_lm = cos_dd.lo - r_sincos.err;
177
178 let sin_upper = sin_dd.hi + sin_lp;
179 let sin_lower = sin_dd.hi + sin_lm;
180 let cos_upper = cos_dd.hi + cos_lp;
181 let cos_lower = cos_dd.hi + cos_lm;
182
183 if sin_upper == sin_lower && cos_upper == cos_lower {
185 return (sin_upper, cos_upper);
186 }
187
188 sincos_hard(y, sin_k, cos_k, sin_upper, sin_lower, cos_upper, cos_lower)
189}
190
191#[cold]
192#[inline(never)]
193#[allow(clippy::too_many_arguments)]
194fn sincos_hard(
195 y: DoubleDouble,
196 sin_k: DoubleDouble,
197 cos_k: DoubleDouble,
198 sin_upper: f64,
199 sin_lower: f64,
200 cos_upper: f64,
201 cos_lower: f64,
202) -> (f64, f64) {
203 let r_sincos = sincos_eval_dd(y);
204
205 let msin_k = -sin_k;
206
207 let sin_x = if sin_upper == sin_lower {
208 sin_upper
209 } else {
210 DoubleDouble::mul_add(sin_k, r_sincos.v_cos, cos_k * r_sincos.v_sin).to_f64()
214 };
215
216 let cos_x = if cos_upper == cos_lower {
217 cos_upper
218 } else {
219 DoubleDouble::mul_add(cos_k, r_sincos.v_cos, msin_k * r_sincos.v_sin).to_f64()
222 };
223 (sin_x, cos_x)
224}
225
226#[cfg(test)]
227mod tests {
228 use super::*;
229
230 #[test]
231 fn f_sincos_test() {
232 let subnormal = f_sincos(0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015708065354637772);
233 assert_eq!(subnormal.0, 1.5708065354637772e-307);
234 assert_eq!(subnormal.1, 1.0);
235 let zx_0 = f_sincos(0.0);
236 assert_eq!(zx_0.0, 0.0);
237 assert_eq!(zx_0.1, 1.0);
238 let zx_1 = f_sincos(1.0);
239 assert_eq!(zx_1.0, 0.8414709848078965);
240 assert_eq!(zx_1.1, 0.5403023058681398);
241 let zx_0_p5 = f_sincos(-0.5);
242 assert_eq!(zx_0_p5.0, -0.479425538604203);
243 assert_eq!(zx_0_p5.1, 0.8775825618903728);
244
245 let zx_1 = f_sincos(0.002341235432);
246 assert_eq!(zx_1.0, 0.0023412332931324344);
247 assert_eq!(zx_1.1, 0.9999972593095778);
248
249 let zx_1 = f_sincos(0.0198676543432);
250 assert_eq!(zx_1.0, 0.019866347330026367);
251 assert_eq!(zx_1.1, 0.9998026446473137);
252 }
253}