| 1 | /* origin: FreeBSD /usr/src/lib/msun/src/s_sin.c */ |
| 2 | /* |
| 3 | * ==================================================== |
| 4 | * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. |
| 5 | * |
| 6 | * Developed at SunPro, a Sun Microsystems, Inc. business. |
| 7 | * Permission to use, copy, modify, and distribute this |
| 8 | * software is freely granted, provided that this notice |
| 9 | * is preserved. |
| 10 | * ==================================================== |
| 11 | */ |
| 12 | |
| 13 | use super::{get_high_word, k_cos, k_sin, rem_pio2}; |
| 14 | |
| 15 | /// Both the sine and cosine of `x` (f64). |
| 16 | /// |
| 17 | /// `x` is specified in radians and the return value is (sin(x), cos(x)). |
| 18 | #[cfg_attr (all(test, assert_no_panic), no_panic::no_panic)] |
| 19 | pub fn sincos(x: f64) -> (f64, f64) { |
| 20 | let s: f64; |
| 21 | let c: f64; |
| 22 | let mut ix: u32; |
| 23 | |
| 24 | ix = get_high_word(x); |
| 25 | ix &= 0x7fffffff; |
| 26 | |
| 27 | /* |x| ~< pi/4 */ |
| 28 | if ix <= 0x3fe921fb { |
| 29 | /* if |x| < 2**-27 * sqrt(2) */ |
| 30 | if ix < 0x3e46a09e { |
| 31 | /* raise inexact if x!=0 and underflow if subnormal */ |
| 32 | let x1p120 = f64::from_bits(0x4770000000000000); // 0x1p120 == 2^120 |
| 33 | if ix < 0x00100000 { |
| 34 | force_eval!(x / x1p120); |
| 35 | } else { |
| 36 | force_eval!(x + x1p120); |
| 37 | } |
| 38 | return (x, 1.0); |
| 39 | } |
| 40 | return (k_sin(x, 0.0, 0), k_cos(x, 0.0)); |
| 41 | } |
| 42 | |
| 43 | /* sincos(Inf or NaN) is NaN */ |
| 44 | if ix >= 0x7ff00000 { |
| 45 | let rv = x - x; |
| 46 | return (rv, rv); |
| 47 | } |
| 48 | |
| 49 | /* argument reduction needed */ |
| 50 | let (n, y0, y1) = rem_pio2(x); |
| 51 | s = k_sin(y0, y1, 1); |
| 52 | c = k_cos(y0, y1); |
| 53 | match n & 3 { |
| 54 | 0 => (s, c), |
| 55 | 1 => (c, -s), |
| 56 | 2 => (-s, -c), |
| 57 | 3 => (-c, s), |
| 58 | #[cfg (debug_assertions)] |
| 59 | _ => unreachable!(), |
| 60 | #[cfg (not(debug_assertions))] |
| 61 | _ => (0.0, 1.0), |
| 62 | } |
| 63 | } |
| 64 | |
| 65 | // These tests are based on those from sincosf.rs |
| 66 | #[cfg (test)] |
| 67 | mod tests { |
| 68 | use super::sincos; |
| 69 | |
| 70 | const TOLERANCE: f64 = 1e-6; |
| 71 | |
| 72 | #[test ] |
| 73 | fn with_pi() { |
| 74 | let (s, c) = sincos(core::f64::consts::PI); |
| 75 | assert!( |
| 76 | (s - 0.0).abs() < TOLERANCE, |
| 77 | "|{} - {}| = {} >= {}" , |
| 78 | s, |
| 79 | 0.0, |
| 80 | (s - 0.0).abs(), |
| 81 | TOLERANCE |
| 82 | ); |
| 83 | assert!( |
| 84 | (c + 1.0).abs() < TOLERANCE, |
| 85 | "|{} + {}| = {} >= {}" , |
| 86 | c, |
| 87 | 1.0, |
| 88 | (s + 1.0).abs(), |
| 89 | TOLERANCE |
| 90 | ); |
| 91 | } |
| 92 | |
| 93 | #[test ] |
| 94 | fn rotational_symmetry() { |
| 95 | use core::f64::consts::PI; |
| 96 | const N: usize = 24; |
| 97 | for n in 0..N { |
| 98 | let theta = 2. * PI * (n as f64) / (N as f64); |
| 99 | let (s, c) = sincos(theta); |
| 100 | let (s_plus, c_plus) = sincos(theta + 2. * PI); |
| 101 | let (s_minus, c_minus) = sincos(theta - 2. * PI); |
| 102 | |
| 103 | assert!( |
| 104 | (s - s_plus).abs() < TOLERANCE, |
| 105 | "|{} - {}| = {} >= {}" , |
| 106 | s, |
| 107 | s_plus, |
| 108 | (s - s_plus).abs(), |
| 109 | TOLERANCE |
| 110 | ); |
| 111 | assert!( |
| 112 | (s - s_minus).abs() < TOLERANCE, |
| 113 | "|{} - {}| = {} >= {}" , |
| 114 | s, |
| 115 | s_minus, |
| 116 | (s - s_minus).abs(), |
| 117 | TOLERANCE |
| 118 | ); |
| 119 | assert!( |
| 120 | (c - c_plus).abs() < TOLERANCE, |
| 121 | "|{} - {}| = {} >= {}" , |
| 122 | c, |
| 123 | c_plus, |
| 124 | (c - c_plus).abs(), |
| 125 | TOLERANCE |
| 126 | ); |
| 127 | assert!( |
| 128 | (c - c_minus).abs() < TOLERANCE, |
| 129 | "|{} - {}| = {} >= {}" , |
| 130 | c, |
| 131 | c_minus, |
| 132 | (c - c_minus).abs(), |
| 133 | TOLERANCE |
| 134 | ); |
| 135 | } |
| 136 | } |
| 137 | } |
| 138 | |