1#![allow(clippy::approx_constant)]
10
11use crate::utils::evalpoly;
12use hexf::hexf64;
13
14const MAXINTFLOAT64: u64 = 1 << f64::MANTISSA_DIGITS;
15const MAXINTFLOAT32: u64 = 1 << f32::MANTISSA_DIGITS;
16
17#[inline]
19fn sinpi_kernel(x: f64) -> f64 {
20 let c0_hi = hexf64!("0x1.921fb54442d18p+1"); let c0_lo = hexf64!("0x1.1a5f14401a52p-53"); let c2 = hexf64!("-0x1.4abbce625be01p2"); let c4 = hexf64!("0x1.466bc67753d55p1"); let c6 = hexf64!("-0x1.32d2ccde1222cp-1"); let c8 = hexf64!("0x1.50782aae65ef4p-4"); let c10 = hexf64!("-0x1.e2fa787f268fep-8"); let c12 = hexf64!("0x1.e06afd55415bp-12"); let c14 = hexf64!("0x1.add9d45ae8195p-17"); let x_square = x * x;
33 let x_bisquare = x_square * x_square;
34
35 let mut r = evalpoly(&[c4, c6, c8, c10, c12, c14], x_square);
37 r = x_bisquare.mul_add(r, c0_lo);
39 r = c2.mul_add(x_square, r);
41 c0_hi.mul_add(x, x * r)
43}
44
45#[inline]
46fn sinpif_kernel(x: f32) -> f32 {
47 let c0 = hexf64!("0x1.921fb6p1"); let c2 = hexf64!("-0x1.4abc1cp2"); let c4 = hexf64!("0x1.468e6cp1"); let c6 = hexf64!("-0x1.3e497cp-1"); let c8 = hexf64!("0x1.eb5482p-3"); let x_f64 = x as f64;
54
55 let res_f64 = x_f64 * evalpoly(&[c0, c2, c4, c6, c8], x_f64 * x_f64);
56 res_f64 as f32
57}
58
59#[inline]
89fn cospi_kernel(x: f64) -> f64 {
90 let c0 = 1.0;
92 let c2_hi = hexf64!("-0x1.3bd3cc9be45dep2"); let c2_lo = hexf64!("0x1.219c35926754dp-52"); let c4 = hexf64!("0x1.03c1f081b5a67p2"); let c6 = hexf64!("-0x1.55d3c7e3c325bp0"); let c8 = hexf64!("0x1.e1f5067b90b37p-3"); let c10 = hexf64!("-0x1.a6d1e7294bffap-6"); let c12 = hexf64!("0x1.f9c89ca1d5187p-10"); let c14 = hexf64!("-0x1.b167302e37198p-14"); let x_square = x * x;
103
104 let r = x_square * evalpoly(&[c4, c6, c8, c10, c12, c14], x_square);
106
107 let a_x_square_hi = c2_hi * x_square;
110 let a_x_square_lo = c2_lo.mul_add(x_square, (-c2_hi).mul_add(x_square, a_x_square_hi));
113
114 let w = c0 + a_x_square_hi;
116
117 w + x_square.mul_add(r, ((c0 - w) + a_x_square_hi) - a_x_square_lo)
121}
122
123#[inline]
124fn cospif_kernel(x: f32) -> f32 {
125 let c0 = 1.0;
126 let c2 = hexf64!("-0x1.3bd3ccp2"); let c4 = hexf64!("0x1.03c1a6p2"); let c6 = hexf64!("-0x1.55a3b4p0"); let c8 = hexf64!("0x1.c85d38p-3"); let c10 = hexf64!("0x1.97cb1p-5"); let x_f64 = x as f64;
133
134 let res_f64 = evalpoly(&[c0, c2, c4, c6, c8, c10], x_f64 * x_f64);
135 res_f64 as f32
136}
137
138pub fn sinpi(x: f64) -> f64 {
144 let x_abs = x.abs();
145 if x_abs.is_infinite() || x_abs.is_nan() {
146 return f64::NAN;
147 }
148 if x_abs >= MAXINTFLOAT64 as f64 {
150 return 0.0f64.copysign(x);
151 }
152
153 let n = (2.0 * x_abs).round();
155 let rx = (-0.5f64).mul_add(n, x_abs);
156 let n = n as i64 & 3;
157 let res = match n {
158 0 => sinpi_kernel(rx),
159 1 => cospi_kernel(rx),
160 2 => 0.0 - sinpi_kernel(rx),
161 _ => 0.0 - cospi_kernel(rx),
162 };
163 if x.is_sign_negative() { -res } else { res }
164}
165
166pub fn sinpif(x: f32) -> f32 {
172 let x_abs = x.abs();
173 if x_abs.is_infinite() || x_abs.is_nan() {
174 return f32::NAN;
175 }
176 if x_abs >= MAXINTFLOAT32 as f32 {
178 return 0.0f32.copysign(x);
179 }
180
181 let n = (2.0 * x_abs).round();
183 let rx = (-0.5f32).mul_add(n, x_abs);
184 let n = n as i64 & 3;
185 let res = match n {
186 0 => sinpif_kernel(rx),
187 1 => cospif_kernel(rx),
188 2 => 0.0 - sinpif_kernel(rx),
189 _ => 0.0 - cospif_kernel(rx),
190 };
191 if x.is_sign_negative() { -res } else { res }
192}
193
194pub fn cospi(x: f64) -> f64 {
200 let x_abs = x.abs();
201 if x_abs.is_infinite() || x_abs.is_nan() {
202 return f64::NAN;
203 }
204 if x_abs >= MAXINTFLOAT64 as f64 {
206 return 1.0;
207 }
208
209 let n = (2.0 * x_abs).round();
211 let rx = (-0.5f64).mul_add(n, x_abs);
212 let n = n as i64 & 3;
213 match n {
214 0 => cospi_kernel(rx),
215 1 => 0.0 - sinpi_kernel(rx),
216 2 => 0.0 - cospi_kernel(rx),
217 _ => sinpi_kernel(rx),
218 }
219}
220
221pub fn cospif(x: f32) -> f32 {
227 let x_abs = x.abs();
228 if x_abs.is_infinite() || x_abs.is_nan() {
229 return f32::NAN;
230 }
231 if x_abs >= MAXINTFLOAT32 as f32 {
233 return 1.0;
234 }
235
236 let n = (2.0 * x_abs).round();
238 let rx = (-0.5f32).mul_add(n, x_abs);
239 let n = n as i64 & 3;
240 match n {
241 0 => cospif_kernel(rx),
242 1 => 0.0 - sinpif_kernel(rx),
243 2 => 0.0 - cospif_kernel(rx),
244 _ => sinpif_kernel(rx),
245 }
246}
247
248pub fn sincospi(x: f64) -> (f64, f64) {
254 let x_abs = x.abs();
255 if x_abs.is_infinite() || x_abs.is_nan() {
256 return (f64::NAN, f64::NAN);
257 }
258 if x_abs >= MAXINTFLOAT64 as f64 {
260 return (0.0f64.copysign(x), 1.0);
261 }
262
263 let n = (2.0 * x_abs).round();
265 let rx = (-0.5f64).mul_add(n, x_abs);
266 let n = n as i64 & 3;
267 let mut si = sinpi_kernel(rx);
268 let mut co = cospi_kernel(rx);
269 (si, co) = match n {
270 0 => (si, co),
271 1 => (co, 0.0 - si),
272 2 => (0.0 - si, 0.0 - co),
273 _ => (0.0 - co, si),
274 };
275 si = if x.is_sign_negative() { -si } else { si };
276 (si, co)
277}
278
279pub fn sincospif(x: f32) -> (f32, f32) {
285 let x_abs = x.abs();
286 if x_abs.is_infinite() || x_abs.is_nan() {
287 return (f32::NAN, f32::NAN);
288 }
289 if x_abs >= MAXINTFLOAT32 as f32 {
291 return (0.0f32.copysign(x), 1.0f32);
292 }
293
294 let n = (2.0 * x_abs).round();
296 let rx = (-0.5f32).mul_add(n, x_abs);
297 let n = n as i64 & 3;
298 let mut si = sinpif_kernel(rx);
299 let mut co = cospif_kernel(rx);
300 (si, co) = match n {
301 0 => (si, co),
302 1 => (co, 0.0 - si),
303 2 => (0.0 - si, 0.0 - co),
304 _ => (0.0 - co, si),
305 };
306 si = if x.is_sign_negative() { -si } else { si };
307 (si, co)
308}
309
310#[cfg(test)]
311mod tests {
312 use super::*;
313 use crate::utils::assert_approx_eq;
314
315 const EPSILON_F64: f64 = 1e-15;
316 const EPSILON_F32: f32 = 1e-6;
317
318 #[test]
319 fn test_maxintfloat64() {
320 assert_eq!(9007199254740992, MAXINTFLOAT64);
321 }
322
323 #[test]
324 fn test_maxintfloat32() {
325 assert_eq!(16777216, MAXINTFLOAT32);
326 }
327
328 #[test]
329 fn test_sinpi_special_values() {
330 for i in -10..=10 {
332 let x = i as f64;
333 assert_eq!(sinpi(x), 0.0, "sinpi({}) should be 0.0", x);
334 }
335
336 assert_approx_eq!(sinpi(0.5), 1.0, EPSILON_F64);
338 assert_approx_eq!(sinpi(1.5), -1.0, EPSILON_F64);
339 assert_approx_eq!(sinpi(-0.5), -1.0, EPSILON_F64);
340 }
341
342 #[test]
343 fn test_cospi_special_values() {
344 assert_approx_eq!(cospi(0.0), 1.0, EPSILON_F64);
346 assert_approx_eq!(cospi(1.0), -1.0, EPSILON_F64);
347 assert_approx_eq!(cospi(2.0), 1.0, EPSILON_F64);
348 assert_approx_eq!(cospi(-1.0), -1.0, EPSILON_F64);
349
350 assert_approx_eq!(cospi(0.5), 0.0, EPSILON_F64);
352 assert_approx_eq!(cospi(1.5), 0.0, EPSILON_F64);
353 assert_approx_eq!(cospi(-0.5), 0.0, EPSILON_F64);
354 }
355
356 #[test]
357 fn test_sincospi_consistency() {
358 let values = [-0.1, 0.2, 0.33, 0.5, 10.7, 1000.123];
359 for &x in &values {
360 let (s, c) = sincospi(x);
361 let s_single = sinpi(x);
362 let c_single = cospi(x);
363 assert_approx_eq!(s, s_single, EPSILON_F64);
364 assert_approx_eq!(c, c_single, EPSILON_F64);
365 }
366 }
367
368 #[test]
369 fn test_pythagorean_identity() {
370 let values = [0.123, 0.456, 1.789, -2.345, 100.0];
372 for &x in &values {
373 let s = sinpi(x);
374 let c = cospi(x);
375 assert_approx_eq!(s * s + c * c, 1.0, EPSILON_F64);
376 }
377 }
378
379 #[test]
380 fn test_nan_inf() {
381 assert!(sinpi(f64::NAN).is_nan());
382 assert!(sinpi(f64::INFINITY).is_nan());
383 assert!(sinpi(f64::NEG_INFINITY).is_nan());
384
385 assert!(cospi(f64::NAN).is_nan());
386 assert!(cospi(f64::INFINITY).is_nan());
387 }
388
389 #[test]
391 fn test_sinpif_special_values() {
392 assert_approx_eq!(sinpif(0.0), 0.0, EPSILON_F32);
393 assert_approx_eq!(sinpif(0.5), 1.0, EPSILON_F32);
394 assert_approx_eq!(sinpif(1.0), 0.0, EPSILON_F32);
395 }
396
397 #[test]
398 fn test_cospif_special_values() {
399 assert_approx_eq!(cospif(0.0), 1.0, EPSILON_F32);
400 assert_approx_eq!(cospif(0.5), 0.0, EPSILON_F32);
401 assert_approx_eq!(cospif(1.0), -1.0, EPSILON_F32);
402 }
403}