1use crate::double_double::DoubleDouble;
30use crate::sincospi::{GenSinCosPiBackend, SinCosPiBackend};
31use crate::tangent::tanpi_table::TANPI_K_PI_OVER_64;
32
33#[inline]
34pub(crate) fn tanpi_eval<B: SinCosPiBackend>(x: f64, backend: &B) -> DoubleDouble {
35 let x2 = DoubleDouble::from_exact_mult(x, x);
36 const C: [u64; 4] = [
41 0x4024abbce625be51,
42 0x404466bc677698e5,
43 0x40645fff70379ae3,
44 0x4084626b091b7fd0,
45 ];
46 const C0: DoubleDouble = DoubleDouble::from_bit_pair((0x3ca1a6444aa5b996, 0x400921fb54442d18));
47
48 let u0 = backend.fma(x2.hi, f64::from_bits(C[1]), f64::from_bits(C[0]));
50 let u1 = backend.fma(x2.hi, f64::from_bits(C[3]), f64::from_bits(C[2]));
51 let tan_poly_lo = backend.fma(x2.hi * x2.hi, u1, u0);
52
53 let r_lo = backend.quick_mult_f64(x2, tan_poly_lo);
56 let tan_lo = backend.fma(r_lo.lo, x, r_lo.hi * x);
57 let tan_hi = backend.quick_mult_f64(C0, x);
58 DoubleDouble::full_add_f64(tan_hi, tan_lo)
59}
60
61#[cold]
62fn tanpi_hard<B: SinCosPiBackend>(x: f64, tan_k: DoubleDouble, backend: &B) -> DoubleDouble {
63 const C: [(u64, u64); 6] = [
64 (0x3ca1a62632712fc8, 0x400921fb54442d18),
65 (0xbcc052338fbb4528, 0x4024abbce625be53),
66 (0x3ced42454c5f85b3, 0x404466bc6775aad9),
67 (0xbd00c7d6a971a560, 0x40645fff9b4b244d),
68 (0x3d205970eff53274, 0x40845f46e96c3a0b),
69 (0xbd3589489ad24fc4, 0x40a4630551cd123d),
70 ];
71 let x2 = backend.exact_mult(x, x);
72 let mut tan_y = backend.quick_mul_add(
73 x2,
74 DoubleDouble::from_bit_pair(C[5]),
75 DoubleDouble::from_bit_pair(C[4]),
76 );
77 tan_y = backend.quick_mul_add(x2, tan_y, DoubleDouble::from_bit_pair(C[3]));
78 tan_y = backend.quick_mul_add(x2, tan_y, DoubleDouble::from_bit_pair(C[2]));
79 tan_y = backend.quick_mul_add(x2, tan_y, DoubleDouble::from_bit_pair(C[1]));
80 tan_y = backend.quick_mul_add(x2, tan_y, DoubleDouble::from_bit_pair(C[0]));
81 tan_y = backend.quick_mult_f64(tan_y, x);
82
83 let num = DoubleDouble::full_dd_add(tan_y, tan_k);
85 let den = backend.mul_add_f64(tan_y, -tan_k, 1.);
87 backend.div(num, den)
89}
90
91#[inline(always)]
92fn tanpi_gen_impl<B: SinCosPiBackend>(x: f64, backend: B) -> f64 {
93 if x == 0. {
94 return x;
95 }
96 let ax = x.to_bits() & 0x7fff_ffff_ffff_ffff;
97 if ax >= (0x7ffu64 << 52) {
98 if ax > (0x7ffu64 << 52) {
100 return x + x;
101 } return f64::NAN; }
104 let e: i32 = (ax >> 52) as i32 - 1023;
105 if e > 0 {
106 if e >= 52 {
107 return f64::copysign(0., x);
109 }
110 let m = (ax & ((1u64 << 52) - 1)) | (1u64 << 52); let shift = 52 - e;
113
114 let frac = m & ((1u64 << shift) - 1);
115 if frac == (1u64 << (shift - 1)) {
116 return f64::INFINITY;
118 }
119 }
120
121 if ax <= 0x3cb0000000000000 {
122 const PI: DoubleDouble =
125 DoubleDouble::from_bit_pair((0x3ca1a62633145c07, 0x400921fb54442d18));
126 if ax <= 0x3ca0000000000000 {
127 let e: i32 = (ax >> 52) as i32;
129 let sc = f64::from_bits((2045i64 - e as i64).wrapping_shl(52) as u64);
130 let isc = f64::from_bits(1i64.wrapping_add(e as i64).wrapping_shl(52) as u64);
131 let dx = x * sc;
132 let q0 = backend.quick_mult_f64(PI, dx);
133 let r = q0.to_f64() * isc;
134 return r;
135 }
136 let q0 = backend.quick_mult_f64(PI, x);
137 let r = q0.to_f64();
138 return r;
139 }
140
141 let (y, k) = backend.arg_reduce_pi_64(x);
143
144 if y == 0.0 {
145 let km = (k.abs() & 63) as i32; match km {
148 0 => return f64::copysign(0f64, x), 32 => return f64::copysign(f64::INFINITY, x), 16 => return f64::copysign(1.0, x), 48 => return -f64::copysign(1.0, x), _ => {}
153 }
154 }
155
156 let tan_k = DoubleDouble::from_bit_pair(TANPI_K_PI_OVER_64[((k as u64) & 127) as usize]);
157
158 let tan_y = tanpi_eval(y, &backend);
161 let num = DoubleDouble::add(tan_k, tan_y);
163 let den = backend.mul_add_f64(tan_y, -tan_k, 1.);
165 let tan_value = backend.div(num, den);
167 let err = backend.fma(
168 tan_value.hi,
169 f64::from_bits(0x3bf0000000000000), f64::from_bits(0x3b60000000000000), );
172 let ub = tan_value.hi + (tan_value.lo + err);
173 let lb = tan_value.hi + (tan_value.lo - err);
174 if ub == lb {
175 return tan_value.to_f64();
176 }
177 tanpi_hard(y, tan_k, &backend).to_f64()
178}
179
180#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
181#[target_feature(enable = "avx", enable = "fma")]
182unsafe fn tanpi_fma_impl(x: f64) -> f64 {
183 use crate::sincospi::FmaSinCosPiBackend;
184 tanpi_gen_impl(x, FmaSinCosPiBackend {})
185}
186
187pub fn f_tanpi(x: f64) -> f64 {
191 #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
192 {
193 tanpi_gen_impl(x, GenSinCosPiBackend {})
194 }
195 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
196 {
197 use std::sync::OnceLock;
198 static EXECUTOR: OnceLock<unsafe fn(f64) -> f64> = OnceLock::new();
199 let q = EXECUTOR.get_or_init(|| {
200 if std::arch::is_x86_feature_detected!("avx")
201 && std::arch::is_x86_feature_detected!("fma")
202 {
203 tanpi_fma_impl
204 } else {
205 fn def_tanpi(x: f64) -> f64 {
206 tanpi_gen_impl(x, GenSinCosPiBackend {})
207 }
208 def_tanpi
209 }
210 });
211 unsafe { q(x) }
212 }
213}
214
215#[cfg(test)]
216mod tests {
217 use super::*;
218
219 #[test]
220 fn test_tanpi() {
221 assert_eq!(f_tanpi(0.4999999999119535), 3615246871.564404);
222 assert_eq!(f_tanpi(7119681148991743.0), 0.);
223 assert_eq!(f_tanpi(63.5), f64::INFINITY);
224 assert_eq!(f_tanpi(63.99935913085936), -0.0020133525045719896);
225 assert_eq!(f_tanpi(3.3821122649309461E-306), 1.0625219045122997E-305);
226 assert_eq!(f_tanpi(1.8010707049867402E-255), 5.6582304953821333E-255);
227 assert_eq!(f_tanpi(1.001000000061801), 0.0031416031832113213);
228 assert_eq!(f_tanpi(-0.5000000000000226), 14054316517702.594);
229 assert_eq!(f_tanpi(0.5000000000000001), -2867080569611329.5);
230 assert_eq!(f_tanpi(0.02131), 0.06704753721009375);
231 assert!(f_tanpi(f64::INFINITY).is_nan());
232 assert!(f_tanpi(f64::NAN).is_nan());
233 assert!(f_tanpi(f64::NEG_INFINITY).is_nan());
234 }
235}