1use crate::double_double::DoubleDouble;
30use crate::sincospi::{GenSinCosPiBackend, SinCosPiBackend, reduce_pi_64};
31use crate::tangent::tanpi::tanpi_eval;
32use crate::tangent::tanpi_table::TANPI_K_PI_OVER_64;
33
34#[cold]
35fn cotpi_hard(x: f64, tan_k: DoubleDouble) -> DoubleDouble {
36 const C: [(u64, u64); 6] = [
37 (0x3ca1a62632712fc8, 0x400921fb54442d18),
38 (0xbcc052338fbb4528, 0x4024abbce625be53),
39 (0x3ced42454c5f85b3, 0x404466bc6775aad9),
40 (0xbd00c7d6a971a560, 0x40645fff9b4b244d),
41 (0x3d205970eff53274, 0x40845f46e96c3a0b),
42 (0xbd3589489ad24fc4, 0x40a4630551cd123d),
43 ];
44 let x2 = DoubleDouble::from_exact_mult(x, x);
45 let mut tan_y = DoubleDouble::quick_mul_add(
46 x2,
47 DoubleDouble::from_bit_pair(C[5]),
48 DoubleDouble::from_bit_pair(C[4]),
49 );
50 tan_y = DoubleDouble::quick_mul_add(x2, tan_y, DoubleDouble::from_bit_pair(C[3]));
51 tan_y = DoubleDouble::quick_mul_add(x2, tan_y, DoubleDouble::from_bit_pair(C[2]));
52 tan_y = DoubleDouble::quick_mul_add(x2, tan_y, DoubleDouble::from_bit_pair(C[1]));
53 tan_y = DoubleDouble::quick_mul_add(x2, tan_y, DoubleDouble::from_bit_pair(C[0]));
54 tan_y = DoubleDouble::quick_mult_f64(tan_y, x);
55
56 let num = DoubleDouble::full_dd_add(tan_y, tan_k);
58 let den = DoubleDouble::mul_add_f64(tan_y, -tan_k, 1.);
60 DoubleDouble::div(den, num)
62}
63
64#[inline(always)]
65fn cotpi_gen_impl<B: SinCosPiBackend>(x: f64, backend: B) -> f64 {
66 if x == 0. {
67 return if x.is_sign_negative() {
68 f64::NEG_INFINITY
69 } else {
70 f64::INFINITY
71 };
72 }
73 let ax = x.to_bits() & 0x7fff_ffff_ffff_ffff;
74 if ax >= (0x7ffu64 << 52) {
75 if ax > (0x7ffu64 << 52) {
77 return x + x;
78 } return f64::NAN; }
81 let e: i32 = (ax >> 52) as i32 - 1023;
82 if e > 0 {
83 if e >= 52 {
84 return f64::copysign(f64::INFINITY, x);
86 }
87 let m = (ax & ((1u64 << 52) - 1)) | (1u64 << 52); let shift = 52 - e;
90
91 let frac = m & ((1u64 << shift) - 1);
92 if frac == (1u64 << (shift - 1)) {
93 return f64::copysign(0., x);
95 }
96 }
97
98 if ax <= 0x3cb0000000000000 {
99 const ONE_OVER_PI: DoubleDouble =
102 DoubleDouble::from_bit_pair((0xbc76b01ec5417056, 0x3fd45f306dc9c883));
103 if ax <= 0x3ca0000000000000 {
104 let e: i32 = (ax >> 52) as i32;
106 let sc = f64::from_bits((2045i64 - e as i64).wrapping_shl(52) as u64);
107 let dx = x * sc;
108 let q0 = backend.quick_mult(ONE_OVER_PI, DoubleDouble::from_quick_recip(dx));
109 let r = q0.to_f64() * sc;
110 return r;
111 }
112 let q0 = backend.quick_mult(ONE_OVER_PI, DoubleDouble::from_quick_recip(x));
113 let r = q0.to_f64();
114 return r;
115 }
116
117 let (y, k) = backend.arg_reduce_pi_64(x);
119
120 if y == 0.0 {
121 let km = (k.abs() & 63) as i32; match km {
124 0 => return f64::copysign(f64::INFINITY, x), 32 => return f64::copysign(0., x), 16 => return f64::copysign(1.0, x), 48 => return -f64::copysign(1.0, x), _ => {}
129 }
130 }
131
132 let tan_k = DoubleDouble::from_bit_pair(TANPI_K_PI_OVER_64[((k as u64) & 127) as usize]);
133
134 let tan_y = tanpi_eval(y, &backend);
137 let num = DoubleDouble::add(tan_k, tan_y);
139 let den = backend.mul_add_f64(tan_y, -tan_k, 1.);
141 let tan_value = backend.div(den, num);
143
144 let err = backend.fma(
145 tan_value.hi,
146 f64::from_bits(0x3bf0000000000000), f64::from_bits(0x3b60000000000000), );
149 let ub = tan_value.hi + (tan_value.lo + err);
150 let lb = tan_value.hi + (tan_value.lo - err);
151 if ub == lb {
152 return tan_value.to_f64();
153 }
154 cotpi_hard(y, tan_k).to_f64()
155}
156
157#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
158#[target_feature(enable = "avx", enable = "fma")]
159unsafe fn cotpi_fma_impl(x: f64) -> f64 {
160 use crate::sincospi::FmaSinCosPiBackend;
161 cotpi_gen_impl(x, FmaSinCosPiBackend {})
162}
163
164pub fn f_cotpi(x: f64) -> f64 {
168 #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
169 {
170 cotpi_gen_impl(x, GenSinCosPiBackend {})
171 }
172 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
173 {
174 use std::sync::OnceLock;
175 static EXECUTOR: OnceLock<unsafe fn(f64) -> f64> = OnceLock::new();
176 let q = EXECUTOR.get_or_init(|| {
177 if std::arch::is_x86_feature_detected!("avx")
178 && std::arch::is_x86_feature_detected!("fma")
179 {
180 cotpi_fma_impl
181 } else {
182 fn def_cotpi(x: f64) -> f64 {
183 cotpi_gen_impl(x, GenSinCosPiBackend {})
184 }
185 def_cotpi
186 }
187 });
188 unsafe { q(x) }
189 }
190}
191
192#[inline]
193pub(crate) fn cotpi_core(x: f64) -> DoubleDouble {
194 let (y, k) = reduce_pi_64(x);
196
197 let tan_k = DoubleDouble::from_bit_pair(TANPI_K_PI_OVER_64[((k as u64) & 127) as usize]);
198
199 let tan_y = tanpi_eval(y, &GenSinCosPiBackend {});
202 let num = DoubleDouble::add(tan_k, tan_y);
204 let den = DoubleDouble::mul_add_f64(tan_y, -tan_k, 1.);
206 DoubleDouble::div(den, num)
208}
209
210#[cfg(test)]
211mod tests {
212 use super::*;
213
214 #[test]
215 fn test_cotpi() {
216 assert_eq!(f_cotpi(3.382112265043898e-306), 9.411570676518013e304);
217 assert_eq!(f_cotpi(0.0431431231), 7.332763436038805);
218 assert_eq!(f_cotpi(-0.0431431231), -7.332763436038805);
219 assert_eq!(f_cotpi(0.52324), -0.07314061937774036);
220 assert!(f_cotpi(f64::INFINITY).is_nan());
221 assert!(f_cotpi(f64::NAN).is_nan());
222 assert!(f_cotpi(f64::NEG_INFINITY).is_nan());
223 }
224}