pxfm/
sin.rs

1/*
2 * // Copyright (c) Radzivon Bartoshyk 6/2025. All rights reserved.
3 * //
4 * // Redistribution and use in source and binary forms, with or without modification,
5 * // are permitted provided that the following conditions are met:
6 * //
7 * // 1.  Redistributions of source code must retain the above copyright notice, this
8 * // list of conditions and the following disclaimer.
9 * //
10 * // 2.  Redistributions in binary form must reproduce the above copyright notice,
11 * // this list of conditions and the following disclaimer in the documentation
12 * // and/or other materials provided with the distribution.
13 * //
14 * // 3.  Neither the name of the copyright holder nor the names of its
15 * // contributors may be used to endorse or promote products derived from
16 * // this software without specific prior written permission.
17 * //
18 * // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 * // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 * // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25 * // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26 * // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 * // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29use 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// For 2^-7 < |x| < 2^16, return k and u such that:
39//   k = round(x * 128/pi)
40//   x mod pi/128 = x - k * pi/128 ~ u.hi + u.lo
41// Error bound:
42//   |(x - k * pi/128) - (u_hi + u_lo)| <= max(ulp(ulp(u_hi)), 2^-119)
43//                                      <= 2^-111.
44#[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 = x - k * (pi/128)
52    // Then |y| < pi / 256
53    // With extra rounding errors, we can bound |y| < 1.6 * 2^-7.
54    let y_hi = f_fmla(kd, f64::from_bits(MPI_OVER_128[0]), x); // Exact
55    // |u.hi| < 1.6*2^-7
56    let u_hi = f_fmla(kd, f64::from_bits(MPI_OVER_128[1]), y_hi);
57
58    let u0 = y_hi - u_hi; // Exact
59    // |u.lo| <= max(ulp(u.hi), |kd * MPI_OVER_128[2]|)
60    let u1 = f_fmla(kd, f64::from_bits(MPI_OVER_128[1]), u0); // Exact
61    let u_lo = f_fmla(kd, f64::from_bits(MPI_OVER_128[2]), u1);
62    // Error bound:
63    // |x - k * pi/128| - (u.hi + u.lo) <= ulp(u.lo)
64    //                                  <= ulp(max(ulp(u.hi), kd*MPI_OVER_128[2]))
65    //                                  <= 2^(-7 - 104) = 2^-111.
66    (DoubleDouble::new(u_lo, u_hi), unsafe {
67        kd.to_int_unchecked::<i64>() as u64 // indeterminate values is always filtered out before this call, as well only lowest bits are used
68    })
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    // Evaluate sin(y) = sin(x - k * (pi/128))
94    // We use the degree-7 Taylor approximation:
95    //   sin(y) ~ y - y^3/3! + y^5/5! - y^7/7!
96    // Then the error is bounded by:
97    //   |sin(y) - (y - y^3/3! + y^5/5! - y^7/7!)| < |y|^9/9! < 2^-54/9! < 2^-72.
98    // For y ~ u_hi + u_lo, fully expanding the polynomial and drop any terms
99    // < ulp(u_hi^3) gives us:
100    //   y - y^3/3! + y^5/5! - y^7/7! = ...
101    // ~ u_hi + u_hi^3 * (-1/6 + u_hi^2 * (1/120 - u_hi^2 * 1/5040)) +
102    //        + u_lo (1 + u_hi^2 * (-1/2 + u_hi^2 / 24))
103    let u_hi_sq = u.hi * u.hi; // Error < ulp(u_hi^2) < 2^(-6 - 52) = 2^-58.
104    // p1 ~ 1/120 + u_hi^2 / 5040.
105    let p1 = f_fmla(
106        u_hi_sq,
107        f64::from_bits(0xbf2a01a01a01a01a),
108        f64::from_bits(0x3f81111111111111),
109    );
110    // q1 ~ -1/2 + u_hi^2 / 24.
111    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    // p2 ~ -1/6 + u_hi^2 (1/120 - u_hi^2 * 1/5040)
118    let p2 = f_fmla(u_hi_sq, p1, f64::from_bits(0xbfc5555555555555));
119    // q2 ~ 1 + u_hi^2 (-1/2 + u_hi^2 / 24)
120    let q2 = f_fmla(u_hi_sq, q1, 1.0);
121    let sin_lo = f_fmla(u_hi_3, p2, u.lo * q2);
122    // Overall, |sin(y) - (u_hi + sin_lo)| < 2*ulp(u_hi^3) < 2^-69.
123
124    // Evaluate cos(y) = cos(x - k * (pi/128))
125    // We use the degree-8 Taylor approximation:
126    //   cos(y) ~ 1 - y^2/2 + y^4/4! - y^6/6! + y^8/8!
127    // Then the error is bounded by:
128    //   |cos(y) - (...)| < |y|^10/10! < 2^-81
129    // For y ~ u_hi + u_lo, fully expanding the polynomial and drop any terms
130    // < ulp(u_hi^3) gives us:
131    //   1 - y^2/2 + y^4/4! - y^6/6! + y^8/8! = ...
132    // ~ 1 - u_hi^2/2 + u_hi^4(1/24 + u_hi^2 (-1/720 + u_hi^2/40320)) +
133    //     + u_hi u_lo (-1 + u_hi^2/6)
134    // We compute 1 - u_hi^2 accurately:
135    //   v_hi + v_lo ~ 1 - u_hi^2/2
136    // with error <= 2^-105.
137    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; // Exact
151        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    // r1 ~ -1/720 + u_hi^2 / 40320
170    let r1 = f_fmla(
171        u_hi_sq,
172        f64::from_bits(0x3efa01a01a01a01a),
173        f64::from_bits(0xbf56c16c16c16c17),
174    );
175    // s1 ~ -1 + u_hi^2 / 6
176    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    // r2 ~ 1/24 + u_hi^2 (-1/720 + u_hi^2 / 40320)
180    let r2 = f_fmla(u_hi_sq, r1, f64::from_bits(0x3fa5555555555555));
181    // s2 ~ v_lo + u_hi * u_lo * (-1 + u_hi^2 / 6)
182    let s2 = f_fmla(u_hi_u_lo, s1, v_lo);
183    let cos_lo = f_fmla(u_hi_4, r2, s2);
184    // Overall, |cos(y) - (v_hi + cos_lo)| < 2*ulp(u_hi^4) < 2^-75.
185
186    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    // k is an integer and -pi / 256 <= y <= pi / 256.
208    // Then sin(x) = sin((k * pi/128 + y)
209    //             = sin(y) * cos(k*pi/128) + cos(y) * sin(k*pi/128)
210
211    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    // Generated by Sollya:
221    // d = [0, 0.0490874];
222    // f_sin = sin(y)/y;
223    // Q = fpminimax(f_sin, [|0, 2, 4, 6, 8, 10, 12|], [|107...|], d, relative, floating);
224    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
248/// Sine for double precision
249///
250/// ULP 0.5
251pub 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        // |x| < 2^32 (with FMA) or |x| < 2^23 (w/o FMA)
262        let ax = x.to_bits() & 0x7fff_ffff_ffff_ffff;
263        if ax <= 0x3fa921fbd34a9550 {
264            // |x| <= 0.0490874
265            if x_e < E_BIAS - 26 {
266                // |x| < 2^-26
267                if x == 0.0 {
268                    // Signed zeros.
269                    return x;
270                }
271
272                // For |x| < 2^-26, |sin(x) - x| < ulp(x)/2.
273                return dyad_fmla(x, f64::from_bits(0xbc90000000000000), x);
274            }
275
276            // Polynomial for sin(x)/x
277            // Generated by Sollya:
278            // d = [0, 0.0490874];
279            // f_sin = sin(y)/y;
280            // Q = fpminimax(f_sin, [|0, 2, 4, 6, 8|], [|1, D...|], d, relative, floating);
281
282            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), // 2^-52
298                f64::from_bits(0x3be0000000000000), // 2^-65
299            );
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        // Small range reduction.
309        (y, k) = range_reduction_small(x);
310    } else {
311        // Inf or NaN
312        if x_e > 2 * E_BIAS {
313            // sin(+-Inf) = NaN
314            return x + f64::NAN;
315        }
316
317        // Large range reduction.
318        (k, y) = argument_reduction.reduce(x);
319    }
320
321    let r_sincos = sincos_eval(y);
322
323    // cos(k * pi/128) = sin(k * pi/128 + pi/2) = sin((k + 64) * pi/128).
324    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    // sin_k_cos_y is always >> cos_k_sin_y
334    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; // (rr.lo + ERR);
341    let r_lower = rr.hi + rlm; // (rr.lo - ERR);
342
343    // Ziv's accuracy test
344    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    // After range reduction, k = round(x * 128 / pi) and y = x - k * (pi / 128).
357    // So k is an integer and -pi / 256 <= y <= pi / 256.
358    // Then sin(x) = sin((k * pi/128 + y)
359    //             = sin(y) * cos(k*pi/128) + cos(y) * sin(k*pi/128)
360
361    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    // Generated by Sollya:
371    // d = [0, 0.0490874];
372    // f_sin = sin(y)/y;
373    // Q = fpminimax(f_sin, [|0, 2, 4, 6, 8, 10|], [|107...|], d, relative, floating);
374    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
395/// Cosine for double precision
396///
397/// ULP 0.5
398pub 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        // |x| < 2^32 (with FMA) or |x| < 2^23 (w/o FMA)
409        let ax = x.to_bits() & 0x7fff_ffff_ffff_ffff;
410        if ax <= 0x3fa921fbd34a9550 {
411            // |x| <= 0.0490874
412            if x_e < E_BIAS - 27 {
413                // |x| < 2^-27
414                if x == 0.0 {
415                    // Signed zeros.
416                    return 1.0;
417                }
418                // For |x| < 2^-26, |sin(x) - x| < ulp(x)/2.
419                return 1.0 - min_normal_f64();
420            }
421
422            // Polynomial for cos(x)
423            // Generated by Sollya:
424            // d = [0, 0.0490874];
425            // f_cos = cos(y);
426            // Q = fpminimax(f_cos, [|0, 2, 4, 6, 8|], [|1, D...|], d, relative, floating);
427
428            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), // 2^-52
444                f64::from_bits(0x3be0000000000000), // 2^-65
445            );
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            // // Small range reduction.
455            (y, k) = range_reduction_small(x);
456        }
457    } else {
458        // Inf or NaN
459        if x_e > 2 * E_BIAS {
460            // cos(+-Inf) = NaN
461            return x + f64::NAN;
462        }
463
464        // Large range reduction.
465        (k, y) = argument_reduction.reduce(x);
466    }
467    let r_sincos = sincos_eval(y);
468
469    // After range reduction, k = round(x * 128 / pi) and y = x - k * (pi / 128).
470    // So k is an integer and -pi / 256 <= y <= pi / 256.
471    // Then cos(x) = cos((k * pi/128 + y)
472    //             = cos(y) * cos(k*pi/128) - sin(y) * sin(k*pi/128)
473    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    // cos_k_cos_y is always >> cos_k_msin_y
482    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; // (rr.lo + ERR);
488    let r_lower = rr.hi + rlm; // (rr.lo - ERR);
489
490    // Ziv's accuracy test
491    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}