1use crate::double_double::DoubleDouble;
30use crate::sin::{get_sin_k_rational, range_reduction_small, sincos_eval};
31use crate::sin_table::SIN_K_PI_OVER_128;
32use crate::sincos_dyadic::{range_reduction_small_f128, sincos_eval_dyadic};
33use crate::sincos_reduce::LargeArgumentReduction;
34
35#[cold]
36fn csc_accurate(x: f64, argument_reduction: &mut LargeArgumentReduction, x_e: u64, k: u64) -> f64 {
37 const EXP_BIAS: u64 = (1u64 << (11 - 1u64)) - 1u64;
38 let u_f128 = if x_e < EXP_BIAS + 16 {
39 range_reduction_small_f128(x)
40 } else {
41 argument_reduction.accurate()
42 };
43
44 let sin_cos = sincos_eval_dyadic(&u_f128);
45
46 let sin_k_f128 = get_sin_k_rational(k);
48 let cos_k_f128 = get_sin_k_rational(k.wrapping_add(64));
49
50 let r = (sin_k_f128 * sin_cos.v_cos) + (cos_k_f128 * sin_cos.v_sin);
53 r.reciprocal().fast_as_f64()
54}
55
56pub fn f_csc(x: f64) -> f64 {
60 let x_e = (x.to_bits() >> 52) & 0x7ff;
61 const E_BIAS: u64 = (1u64 << (11 - 1u64)) - 1u64;
62
63 let y: DoubleDouble;
64 let k;
65
66 let mut argument_reduction = LargeArgumentReduction::default();
67
68 if x_e < E_BIAS + 16 {
70 if x_e < E_BIAS - 26 {
72 if x == 0.0 {
74 return if x.is_sign_negative() {
75 f64::NEG_INFINITY
76 } else {
77 f64::INFINITY
78 };
79 }
80
81 if x_e < E_BIAS - 52 {
82 return 1. / x;
83 }
84
85 let rcp = DoubleDouble::from_quick_recip(x);
87 return DoubleDouble::f64_mul_f64_add(x, f64::from_bits(0x3fc5555555555555), rcp)
88 .to_f64();
89 }
90
91 (y, k) = range_reduction_small(x);
93 } else {
94 if x_e > 2 * E_BIAS {
96 return x + f64::NAN;
98 }
99
100 (k, y) = argument_reduction.reduce(x);
102 }
103
104 let r_sincos = sincos_eval(y);
105
106 let sk = SIN_K_PI_OVER_128[(k & 255) as usize];
109 let ck = SIN_K_PI_OVER_128[((k.wrapping_add(64)) & 255) as usize];
110
111 let sin_k = DoubleDouble::from_bit_pair(sk);
112 let cos_k = DoubleDouble::from_bit_pair(ck);
113
114 let sin_k_cos_y = DoubleDouble::quick_mult(r_sincos.v_cos, sin_k);
115 let cos_k_sin_y = DoubleDouble::quick_mult(r_sincos.v_sin, cos_k);
116
117 let mut rr = DoubleDouble::from_exact_add(sin_k_cos_y.hi, cos_k_sin_y.hi);
119 rr.lo += sin_k_cos_y.lo + cos_k_sin_y.lo;
120
121 rr = DoubleDouble::from_exact_add(rr.hi, rr.lo);
122 rr = rr.recip();
123
124 let rlp = rr.lo + r_sincos.err;
125 let rlm = rr.lo - r_sincos.err;
126
127 let r_upper = rr.hi + rlp; let r_lower = rr.hi + rlm; if r_upper == r_lower {
132 return rr.to_f64();
133 }
134
135 csc_accurate(x, &mut argument_reduction, x_e, k)
136}
137
138#[cfg(test)]
139mod tests {
140 use super::*;
141
142 #[test]
143 fn test_csc() {
144 assert_eq!(f_csc(0.000000014901161055069778), 67108864.62500001);
145 assert_eq!(f_csc( 0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000541722315998), f64::INFINITY);
146 assert_eq!(f_csc(0.0), f64::INFINITY);
147 assert_eq!(f_csc(-0.0), f64::NEG_INFINITY);
148 assert!(f_csc(f64::NAN).is_nan());
149 assert_eq!(f_csc(1.0), 1.1883951057781212);
150 assert_eq!(f_csc(-0.5), -2.085829642933488);
151 }
152}