1use std::f64::consts::{FRAC_PI_2, FRAC_PI_4, PI};
20
21const PIO2_HI: f64 = f64::from_bits(0x3FF921FB54442D18); const PIO2_LO: f64 = f64::from_bits(0x3C91A62633145C07); const S1: f64 = f64::from_bits(0xBFC5555555555549); const S2: f64 = f64::from_bits(0x3F8111111110F8A6); const S3: f64 = f64::from_bits(0xBF2A01A019C161D5); const S4: f64 = f64::from_bits(0x3EC71DE357B1FE7D); const S5: f64 = f64::from_bits(0xBE5AE5E68A2B9CEB); const S6: f64 = f64::from_bits(0x3DE5D93A5ACFD57C); const C1: f64 = f64::from_bits(0x3FA5555555555549); const C2: f64 = f64::from_bits(0xBF56C16C16C15177); const C3: f64 = f64::from_bits(0x3EFA01A019CB1590); const C4: f64 = f64::from_bits(0xBE927E4F809C52AD); const C5: f64 = f64::from_bits(0x3E21EE9EBDB4B1C4); const C6: f64 = f64::from_bits(0xBDA8FAE9BE8838D4); const AT: [f64; 11] = [
61 f64::from_bits(0x3FD555555555550D), f64::from_bits(0xBFC999999998EBC4), f64::from_bits(0x3FC24924920083FF), f64::from_bits(0xBFBC71C6FE231671), f64::from_bits(0x3FB745CDC54C206E), f64::from_bits(0xBFB3B0F2AF749A6D), f64::from_bits(0x3FB10D66A0D03D51), f64::from_bits(0xBFADDE2D52DEFD9A), f64::from_bits(0x3FA97B4B24760DEB), f64::from_bits(0xBFA2B4442C6A6C2F), f64::from_bits(0x3F90AD3AE322DA11), ];
73
74const ATAN_REF: [f64; 4] = [
77 4.636476090008061e-01, FRAC_PI_4, 9.827_937_232_473_29e-1, FRAC_PI_2, ];
82
83const EXP_O_THRESHOLD: f64 = f64::from_bits(0x40862E42FEFA39EF); const EXP_U_THRESHOLD: f64 = f64::from_bits(0xC0874910D52D3051); const EXP_LN2HI: f64 = f64::from_bits(0x3FE62E42FEE00000); const EXP_LN2LO: f64 = f64::from_bits(0x3DEA39EF35793C76); const EXP_INVLN2: f64 = f64::from_bits(0x3FF71547652B82FE); const EXP_TWOM1000: f64 = f64::from_bits(0x0170000000000000); const EXP_P1: f64 = f64::from_bits(0x3FC555555555553E); const EXP_P2: f64 = f64::from_bits(0xBF66C16C16BEBD93); const EXP_P3: f64 = f64::from_bits(0x3F11566AAF25DE2C); const EXP_P4: f64 = f64::from_bits(0xBEBBBD41C5D26BF1); const EXP_P5: f64 = f64::from_bits(0x3E66376972BEA4D0); #[inline]
109fn sin_kern(x: f64) -> f64 {
110 let z = x * x;
111 let v = z * x;
112 let r = S2 + z * (S3 + z * (S4 + z * (S5 + z * S6)));
113 x + v * (S1 + z * r)
114}
115
116#[inline]
119fn cos_kern(x: f64) -> f64 {
120 let z = x * x;
121 let r = z * (C1 + z * (C2 + z * (C3 + z * (C4 + z * (C5 + z * C6)))));
122 let hz = 0.5 * z;
123 1.0 - (hz - z * r)
125}
126
127#[inline]
132fn reduce(x: f64) -> (f64, i32) {
133 let n = (x * (2.0 / PI) + 0.5).floor();
134 let r = (x - n * PIO2_HI) - n * PIO2_LO;
135 (r, (n as i64 & 3) as i32)
136}
137
138pub fn det_sin(x: f64) -> f64 {
144 if x.is_nan() || x.is_infinite() {
145 return f64::NAN;
146 }
147 let (r, q) = reduce(x);
148 match q {
149 0 => sin_kern(r),
150 1 => cos_kern(r),
151 2 => -sin_kern(r),
152 3 => -cos_kern(r),
153 _ => unreachable!(),
154 }
155}
156
157pub fn det_cos(x: f64) -> f64 {
159 if x.is_nan() || x.is_infinite() {
160 return f64::NAN;
161 }
162 let (r, q) = reduce(x);
163 match q {
164 0 => cos_kern(r),
165 1 => -sin_kern(r),
166 2 => -cos_kern(r),
167 3 => sin_kern(r),
168 _ => unreachable!(),
169 }
170}
171
172pub fn det_sincos(x: f64) -> (f64, f64) {
174 if x.is_nan() || x.is_infinite() {
175 return (f64::NAN, f64::NAN);
176 }
177 let (r, q) = reduce(x);
178 let s = sin_kern(r);
179 let c = cos_kern(r);
180 match q {
181 0 => ( s, c),
182 1 => ( c, -s),
183 2 => (-s, -c),
184 3 => (-c, s),
185 _ => unreachable!(),
186 }
187}
188
189fn det_atan(x: f64) -> f64 {
195 if x.is_nan() {
196 return f64::NAN;
197 }
198 let neg = x < 0.0;
199 let mut xa = x.abs();
200
201 let id: i32;
203 if xa < 0.4375 {
204 if xa < 1e-29 {
206 return x; }
208 id = -1;
209 } else if xa < 1.1875 {
210 if xa < 0.6875 {
211 id = 0;
213 xa = (2.0 * xa - 1.0) / (2.0 + xa);
214 } else {
215 id = 1;
217 xa = (xa - 1.0) / (xa + 1.0);
218 }
219 } else if xa < 2.4375 {
220 id = 2;
222 xa = (xa - 1.5) / (1.0 + 1.5 * xa);
223 } else {
224 id = 3;
226 xa = -1.0 / xa;
227 }
228
229 let z = xa * xa;
231 let w = z * z;
232 let s1 = z * (AT[0] + w * (AT[2] + w * (AT[4] + w * (AT[6] + w * (AT[8] + w * AT[10])))));
233 let s2 = w * (AT[1] + w * (AT[3] + w * (AT[5] + w * (AT[7] + w * AT[9]))));
234
235 let result = if id < 0 {
236 xa - xa * (s1 + s2)
237 } else {
238 ATAN_REF[id as usize] + (xa - xa * (s1 + s2))
239 };
240
241 if neg { -result } else { result }
242}
243
244pub fn det_atan2(y: f64, x: f64) -> f64 {
246 if y.is_nan() || x.is_nan() {
247 return f64::NAN;
248 }
249
250 if y == 0.0 {
251 if x > 0.0 || (x == 0.0 && x.is_sign_positive()) {
252 return y; } else {
254 return if y.is_sign_negative() { -PI } else { PI };
255 }
256 }
257
258 if x == 0.0 {
259 return if y > 0.0 { FRAC_PI_2 } else { -FRAC_PI_2 };
260 }
261
262 if y.is_infinite() {
263 if x.is_infinite() {
264 return if x > 0.0 {
265 if y > 0.0 { FRAC_PI_4 } else { -FRAC_PI_4 }
266 } else if y > 0.0 { 3.0 * FRAC_PI_4 } else { -3.0 * FRAC_PI_4 };
267 }
268 return if y > 0.0 { FRAC_PI_2 } else { -FRAC_PI_2 };
269 }
270
271 if x.is_infinite() {
272 return if x > 0.0 {
273 if y.is_sign_negative() { -0.0 } else { 0.0 }
274 } else if y.is_sign_negative() { -PI } else { PI };
275 }
276
277 let a = det_atan((y / x).abs());
279
280 if x > 0.0 {
281 if y >= 0.0 { a } else { -a }
282 } else if y >= 0.0 { PI - a } else { -(PI - a) }
283}
284
285pub fn det_hypot(x: f64, y: f64) -> f64 {
290 let xa = x.abs();
291 let ya = y.abs();
292 let (big, small) = if xa >= ya { (xa, ya) } else { (ya, xa) };
293 if big == 0.0 {
294 return 0.0;
295 }
296 let ratio = small / big;
297 big * (1.0 + ratio * ratio).sqrt()
298}
299
300pub fn det_exp(x: f64) -> f64 {
313 if x.is_nan() {
314 return f64::NAN;
315 }
316 if x.is_infinite() {
317 return if x > 0.0 { f64::INFINITY } else { 0.0 };
318 }
319 if x > EXP_O_THRESHOLD {
320 return f64::INFINITY; }
322 if x < EXP_U_THRESHOLD {
323 return 0.0; }
325
326 let hx_abs = (x.abs().to_bits() >> 32) as u32;
329
330 let (hi, lo, k): (f64, f64, i32) = if hx_abs > 0x3fd62e42 {
332 if hx_abs < 0x3FF0A2B2 {
334 if x.is_sign_negative() {
336 (x + EXP_LN2HI, -EXP_LN2LO, -1)
337 } else {
338 (x - EXP_LN2HI, EXP_LN2LO, 1)
339 }
340 } else {
341 let half = if x.is_sign_negative() { -0.5 } else { 0.5 };
343 let k_int = (EXP_INVLN2 * x + half) as i32;
344 let t = k_int as f64;
345 (x - t * EXP_LN2HI, t * EXP_LN2LO, k_int)
346 }
347 } else if hx_abs < 0x3e300000 {
348 return 1.0 + x;
350 } else {
351 (x, 0.0, 0)
353 };
354
355 let r = hi - lo;
356 let z = r * r;
357 let c = r - z * (EXP_P1 + z * (EXP_P2 + z * (EXP_P3 + z * (EXP_P4 + z * EXP_P5))));
358
359 let y = if k == 0 {
360 1.0 - ((r * c) / (c - 2.0) - r)
361 } else {
362 1.0 - ((lo - (r * c) / (2.0 - c)) - hi)
363 };
364
365 if k == 0 {
366 return y;
367 }
368
369 if k >= -1021 {
373 let bits = y.to_bits().wrapping_add(((k as i64) << 52) as u64);
374 f64::from_bits(bits)
375 } else {
376 let bits = y.to_bits().wrapping_add((((k + 1000) as i64) << 52) as u64);
377 f64::from_bits(bits) * EXP_TWOM1000
378 }
379}
380
381pub fn det_powi_f64(base: f64, n: i32) -> f64 {
393 if n == 0 {
394 return 1.0;
395 }
396 if base == 1.0 {
397 return 1.0;
398 }
399 let mut e: u64 = (n as i64).unsigned_abs();
400 let mut b = base;
401 let mut result = 1.0;
402 while e > 0 {
403 if e & 1 == 1 {
404 result *= b;
405 }
406 e >>= 1;
407 if e > 0 {
408 b *= b;
409 }
410 }
411 if n < 0 { 1.0 / result } else { result }
412}
413
414#[cfg(test)]
419mod tests {
420 use super::*;
421 use std::f64::consts::{FRAC_PI_3, FRAC_PI_6};
422
423 fn approx_eq(a: f64, b: f64, tol: f64) -> bool {
424 if a.is_nan() && b.is_nan() { return true; }
425 (a - b).abs() <= tol
426 }
427
428 #[test]
429 fn sin_exact_values() {
430 let tol = 1e-15;
431 assert!(approx_eq(det_sin(0.0), 0.0, tol));
432 assert!(approx_eq(det_sin(FRAC_PI_6), 0.5, tol));
433 assert!(approx_eq(det_sin(FRAC_PI_4), std::f64::consts::FRAC_1_SQRT_2, tol));
434 assert!(approx_eq(det_sin(FRAC_PI_3), 3.0_f64.sqrt() / 2.0, tol));
435 assert!(approx_eq(det_sin(FRAC_PI_2), 1.0, tol));
436 assert!(approx_eq(det_sin(PI), 0.0, 1e-15));
437 assert!(approx_eq(det_sin(-FRAC_PI_2), -1.0, tol));
438 }
439
440 #[test]
441 fn cos_exact_values() {
442 let tol = 1e-15;
443 assert!(approx_eq(det_cos(0.0), 1.0, tol));
444 assert!(approx_eq(det_cos(FRAC_PI_6), 3.0_f64.sqrt() / 2.0, tol));
445 assert!(approx_eq(det_cos(FRAC_PI_4), std::f64::consts::FRAC_1_SQRT_2, tol));
446 assert!(approx_eq(det_cos(FRAC_PI_3), 0.5, tol));
447 assert!(approx_eq(det_cos(FRAC_PI_2), 0.0, 1e-15));
448 assert!(approx_eq(det_cos(PI), -1.0, tol));
449 }
450
451 #[test]
452 fn sincos_identity() {
453 for i in 0..200 {
455 let x = (i as f64 - 100.0) * 0.13;
456 let (s, c) = det_sincos(x);
457 let err = (s * s + c * c - 1.0).abs();
458 assert!(err < 1e-14, "sin²+cos²={} at x={x} (err={err})", s * s + c * c);
459 }
460 }
461
462 #[test]
463 fn sincos_matches_separate() {
464 for i in 0..50 {
465 let x = (i as f64 - 25.0) * 0.37;
466 let (s, c) = det_sincos(x);
467 assert_eq!(s, det_sin(x), "sin mismatch at x={x}");
468 assert_eq!(c, det_cos(x), "cos mismatch at x={x}");
469 }
470 }
471
472 #[test]
473 fn sin_large_argument() {
474 let x = 1e6;
475 let s = det_sin(x);
476 let c = det_cos(x);
477 let err = (s * s + c * c - 1.0).abs();
478 assert!(err < 1e-6, "sin²+cos²={} at x={x}", s * s + c * c);
479 }
480
481 #[test]
482 fn sin_special_values() {
483 assert!(det_sin(f64::NAN).is_nan());
484 assert!(det_sin(f64::INFINITY).is_nan());
485 assert!(det_sin(f64::NEG_INFINITY).is_nan());
486 }
487
488 #[test]
489 fn atan2_quadrants() {
490 let eps = 1e-15;
491 assert!(approx_eq(det_atan2(1.0, 1.0), FRAC_PI_4, eps));
492 assert!(approx_eq(det_atan2(1.0, -1.0), 3.0 * FRAC_PI_4, eps));
493 assert!(approx_eq(det_atan2(-1.0, -1.0), -3.0 * FRAC_PI_4, eps));
494 assert!(approx_eq(det_atan2(-1.0, 1.0), -FRAC_PI_4, eps));
495 assert!(approx_eq(det_atan2(0.0, 1.0), 0.0, eps));
496 assert!(approx_eq(det_atan2(1.0, 0.0), FRAC_PI_2, eps));
497 assert!(approx_eq(det_atan2(0.0, -1.0), PI, eps));
498 assert!(approx_eq(det_atan2(-1.0, 0.0), -FRAC_PI_2, eps));
499 }
500
501 #[test]
502 fn atan2_special_values() {
503 assert!(det_atan2(f64::NAN, 1.0).is_nan());
504 assert!(det_atan2(1.0, f64::NAN).is_nan());
505 }
506
507 #[test]
508 fn atan_specific_values() {
509 let eps = 1e-15;
510 assert!(approx_eq(det_atan(0.0), 0.0, eps));
511 assert!(approx_eq(det_atan(1.0), FRAC_PI_4, eps));
512 assert!(approx_eq(det_atan(-1.0), -FRAC_PI_4, eps));
513 assert!(approx_eq(det_atan(3.0_f64.sqrt()), FRAC_PI_3, eps));
515 assert!(approx_eq(det_atan(1.0 / 3.0_f64.sqrt()), FRAC_PI_6, eps));
517 }
518
519 #[test]
520 fn hypot_basic() {
521 assert!(approx_eq(det_hypot(3.0, 4.0), 5.0, 1e-15));
522 assert!(approx_eq(det_hypot(0.0, 0.0), 0.0, 0.0));
523 assert!(approx_eq(det_hypot(1.0, 0.0), 1.0, 0.0));
524 assert!(approx_eq(det_hypot(0.0, 1.0), 1.0, 0.0));
525 }
526
527 #[test]
528 fn hypot_no_overflow() {
529 let big = 1e300;
530 let h = det_hypot(big, big);
531 assert!(h.is_finite());
532 assert!(approx_eq(h, big * 2.0_f64.sqrt(), big * 1e-14));
533 }
534
535 #[test]
536 fn deterministic_across_calls() {
537 for i in 0..100 {
538 let x = (i as f64) * 0.0731 - 3.5;
539 assert_eq!(det_sin(x).to_bits(), det_sin(x).to_bits());
540 assert_eq!(det_cos(x).to_bits(), det_cos(x).to_bits());
541 }
542 }
543
544 #[test]
545 fn matches_std_closely() {
546 for i in 0..200 {
547 let x = (i as f64 - 100.0) * 0.05;
548 let ds = det_sin(x);
549 let ss = x.sin();
550 assert!((ds - ss).abs() < 5e-13,
551 "det_sin({x})={ds} vs std sin={ss}, diff={}", (ds - ss).abs());
552 let dc = det_cos(x);
553 let sc = x.cos();
554 assert!((dc - sc).abs() < 5e-13,
555 "det_cos({x})={dc} vs std cos={sc}, diff={}", (dc - sc).abs());
556 }
557 }
558
559 #[test]
560 fn exp_exact_values() {
561 let tol = 1e-15;
562 assert_eq!(det_exp(0.0).to_bits(), 1.0_f64.to_bits());
563 assert_eq!(det_exp(-0.0).to_bits(), 1.0_f64.to_bits());
564 assert!(approx_eq(det_exp(1.0), std::f64::consts::E, tol));
565 assert!(approx_eq(det_exp(-1.0), 1.0 / std::f64::consts::E, tol));
566 assert!(approx_eq(det_exp(2.0), std::f64::consts::E.powi(2), 1e-14));
567 assert!(approx_eq(det_exp(-3.0), 0.049787068367863944, 1e-15));
568 assert!(approx_eq(det_exp(0.5), 1.6487212707001282, tol));
569 }
570
571 #[test]
572 fn exp_special_values() {
573 assert!(det_exp(f64::NAN).is_nan());
574 assert_eq!(det_exp(f64::INFINITY), f64::INFINITY);
575 assert_eq!(det_exp(f64::NEG_INFINITY), 0.0);
576 assert_eq!(det_exp(800.0), f64::INFINITY);
578 assert_eq!(det_exp(-800.0), 0.0);
579 }
580
581 #[test]
582 fn exp_tiny_arguments() {
583 let tiny = 1e-12;
585 let res = det_exp(tiny);
586 assert!((res - (1.0 + tiny)).abs() < 1e-24);
587 let neg_tiny = -1e-12;
588 let res = det_exp(neg_tiny);
589 assert!((res - (1.0 + neg_tiny)).abs() < 1e-24);
590 }
591
592 #[test]
593 fn exp_matches_std_closely() {
594 for i in 0..200 {
596 let x = (i as f64 - 100.0) * 0.07;
597 let de = det_exp(x);
598 let se = x.exp();
599 let rel = if se != 0.0 { ((de - se) / se).abs() } else { (de - se).abs() };
600 assert!(rel < 5e-15,
601 "det_exp({x})={de} vs std exp={se}, rel={rel}");
602 }
603 }
604
605 #[test]
606 fn exp_deterministic_across_calls() {
607 for i in 0..100 {
608 let x = (i as f64) * 0.0731 - 3.5;
609 assert_eq!(det_exp(x).to_bits(), det_exp(x).to_bits());
610 }
611 }
612
613 #[test]
614 fn powi_zero_exponent() {
615 assert_eq!(det_powi_f64(0.0, 0).to_bits(), 1.0_f64.to_bits());
617 assert_eq!(det_powi_f64(1.0, 0).to_bits(), 1.0_f64.to_bits());
618 assert_eq!(det_powi_f64(-3.5, 0).to_bits(), 1.0_f64.to_bits());
619 assert_eq!(det_powi_f64(1e300, 0).to_bits(), 1.0_f64.to_bits());
620 }
621
622 #[test]
623 fn powi_one_exponent() {
624 for &b in &[0.0, 1.0, -1.0, 2.5, -7.3, 1e-10, 1e10] {
625 assert_eq!(det_powi_f64(b, 1).to_bits(), b.to_bits(),
626 "powi({b}, 1) should equal {b}");
627 }
628 }
629
630 #[test]
631 fn powi_basic() {
632 assert_eq!(det_powi_f64(2.0, 10), 1024.0);
633 assert_eq!(det_powi_f64(2.0, 16), 65536.0);
634 assert_eq!(det_powi_f64(0.5, 3), 0.125);
635 assert_eq!(det_powi_f64(0.75, 4), 0.31640625);
636 assert_eq!(det_powi_f64(-1.0, 2), 1.0);
637 assert_eq!(det_powi_f64(-1.0, 3), -1.0);
638 assert_eq!(det_powi_f64(-2.0, 4), 16.0);
639 }
640
641 #[test]
642 fn powi_negative_exponent() {
643 assert_eq!(det_powi_f64(2.0, -1), 0.5);
644 assert_eq!(det_powi_f64(2.0, -10), 1.0 / 1024.0);
645 assert_eq!(det_powi_f64(0.5, -3), 8.0);
646 assert_eq!(det_powi_f64(2.0, -8).to_bits(), (1.0_f64 / 256.0).to_bits());
648 }
649
650 #[test]
651 fn powi_matches_std_closely() {
652 for k in -10..=10 {
655 let d = det_powi_f64(2.0, k);
656 let s = 2.0_f64.powi(k);
657 assert_eq!(d.to_bits(), s.to_bits(),
658 "powi(2.0, {k}): det={d} vs std={s}");
659 }
660 for &b in &[0.75, 1.5, 0.9, 1.1, 3.7] {
662 for k in 0..=12 {
663 let d = det_powi_f64(b, k);
664 let s = b.powi(k);
665 let rel = ((d - s) / s).abs();
666 assert!(rel < 5e-15,
667 "powi({b}, {k}): det={d} vs std={s}, rel={rel}");
668 }
669 }
670 }
671
672 #[test]
673 fn powi_deterministic_across_calls() {
674 for k in -20..=20 {
675 for &b in &[0.5, 0.75, 1.5, 2.0, 3.0] {
676 assert_eq!(det_powi_f64(b, k).to_bits(),
677 det_powi_f64(b, k).to_bits());
678 }
679 }
680 }
681}