1use crate::types::widths::D38;
53use crate::types::log_exp::STRICT_GUARD;
54
55impl<const SCALE: u32> D38<SCALE> {
56 #[cfg(all(feature = "strict", not(feature = "fast")))]
59 #[inline]
60 #[must_use]
61 pub fn sin(self) -> Self {
62 self.sin_strict()
63 }
64
65 #[cfg(all(feature = "strict", not(feature = "fast")))]
66 #[inline]
67 #[must_use]
68 pub fn cos(self) -> Self {
69 self.cos_strict()
70 }
71
72 #[cfg(all(feature = "strict", not(feature = "fast")))]
73 #[inline]
74 #[must_use]
75 pub fn tan(self) -> Self {
76 self.tan_strict()
77 }
78
79 #[cfg(all(feature = "strict", not(feature = "fast")))]
80 #[inline]
81 #[must_use]
82 pub fn asin(self) -> Self {
83 self.asin_strict()
84 }
85
86 #[cfg(all(feature = "strict", not(feature = "fast")))]
87 #[inline]
88 #[must_use]
89 pub fn acos(self) -> Self {
90 self.acos_strict()
91 }
92
93 #[cfg(all(feature = "strict", not(feature = "fast")))]
94 #[inline]
95 #[must_use]
96 pub fn atan(self) -> Self {
97 self.atan_strict()
98 }
99
100 #[cfg(all(feature = "strict", not(feature = "fast")))]
101 #[inline]
102 #[must_use]
103 pub fn atan2(self, other: Self) -> Self {
104 self.atan2_strict(other)
105 }
106
107 #[cfg(all(feature = "strict", not(feature = "fast")))]
108 #[inline]
109 #[must_use]
110 pub fn sinh(self) -> Self {
111 self.sinh_strict()
112 }
113
114 #[cfg(all(feature = "strict", not(feature = "fast")))]
115 #[inline]
116 #[must_use]
117 pub fn cosh(self) -> Self {
118 self.cosh_strict()
119 }
120
121 #[cfg(all(feature = "strict", not(feature = "fast")))]
122 #[inline]
123 #[must_use]
124 pub fn tanh(self) -> Self {
125 self.tanh_strict()
126 }
127
128 #[cfg(all(feature = "strict", not(feature = "fast")))]
129 #[inline]
130 #[must_use]
131 pub fn asinh(self) -> Self {
132 self.asinh_strict()
133 }
134
135 #[cfg(all(feature = "strict", not(feature = "fast")))]
136 #[inline]
137 #[must_use]
138 pub fn acosh(self) -> Self {
139 self.acosh_strict()
140 }
141
142 #[cfg(all(feature = "strict", not(feature = "fast")))]
143 #[inline]
144 #[must_use]
145 pub fn atanh(self) -> Self {
146 self.atanh_strict()
147 }
148
149 #[cfg(all(feature = "strict", not(feature = "fast")))]
150 #[inline]
151 #[must_use]
152 pub fn to_degrees(self) -> Self {
153 self.to_degrees_strict()
154 }
155
156 #[cfg(all(feature = "strict", not(feature = "fast")))]
157 #[inline]
158 #[must_use]
159 pub fn to_radians(self) -> Self {
160 self.to_radians_strict()
161 }
162
163 #[inline]
167 #[must_use]
168 pub fn sin_strict(self) -> Self {
169 self.sin_strict_with(crate::support::rounding::DEFAULT_ROUNDING_MODE)
170 }
171
172 #[inline]
173 #[must_use]
174 pub fn sin_strict_with(self, mode: crate::support::rounding::RoundingMode) -> Self {
175 <Self as crate::policy::trig::TrigPolicy>::sin_impl(self, mode)
176 }
177
178 #[inline]
179 #[must_use]
180 pub fn sin_approx(self, working_digits: u32) -> Self {
181 self.sin_approx_with(working_digits, crate::support::rounding::DEFAULT_ROUNDING_MODE)
182 }
183
184 #[inline]
185 #[must_use]
186 pub fn sin_approx_with(self, working_digits: u32, mode: crate::support::rounding::RoundingMode) -> Self {
187 if working_digits == STRICT_GUARD {
188 return self.sin_strict_with(mode);
189 }
190 <Self as crate::policy::trig::TrigPolicy>::sin_with_impl(self, working_digits, mode)
191 }
192
193 #[inline]
195 #[must_use]
196 pub fn cos_strict(self) -> Self {
197 self.cos_strict_with(crate::support::rounding::DEFAULT_ROUNDING_MODE)
198 }
199
200 #[inline]
201 #[must_use]
202 pub fn cos_strict_with(self, mode: crate::support::rounding::RoundingMode) -> Self {
203 <Self as crate::policy::trig::TrigPolicy>::cos_impl(self, mode)
204 }
205
206 #[inline]
207 #[must_use]
208 pub fn cos_approx(self, working_digits: u32) -> Self {
209 self.cos_approx_with(working_digits, crate::support::rounding::DEFAULT_ROUNDING_MODE)
210 }
211
212 #[inline]
213 #[must_use]
214 pub fn cos_approx_with(self, working_digits: u32, mode: crate::support::rounding::RoundingMode) -> Self {
215 if working_digits == STRICT_GUARD {
216 return self.cos_strict_with(mode);
217 }
218 <Self as crate::policy::trig::TrigPolicy>::cos_with_impl(self, working_digits, mode)
219 }
220
221 #[inline]
223 #[must_use]
224 pub fn tan_strict(self) -> Self {
225 self.tan_strict_with(crate::support::rounding::DEFAULT_ROUNDING_MODE)
226 }
227
228 #[inline]
229 #[must_use]
230 pub fn tan_strict_with(self, mode: crate::support::rounding::RoundingMode) -> Self {
231 <Self as crate::policy::trig::TrigPolicy>::tan_impl(self, mode)
232 }
233
234 #[inline]
235 #[must_use]
236 pub fn tan_approx(self, working_digits: u32) -> Self {
237 self.tan_approx_with(working_digits, crate::support::rounding::DEFAULT_ROUNDING_MODE)
238 }
239
240 #[inline]
241 #[must_use]
242 pub fn tan_approx_with(self, working_digits: u32, mode: crate::support::rounding::RoundingMode) -> Self {
243 if working_digits == STRICT_GUARD {
244 return self.tan_strict_with(mode);
245 }
246 <Self as crate::policy::trig::TrigPolicy>::tan_with_impl(self, working_digits, mode)
247 }
248
249 #[inline]
251 #[must_use]
252 pub fn atan_strict(self) -> Self {
253 self.atan_strict_with(crate::support::rounding::DEFAULT_ROUNDING_MODE)
254 }
255
256 #[inline]
257 #[must_use]
258 pub fn atan_strict_with(self, mode: crate::support::rounding::RoundingMode) -> Self {
259 <Self as crate::policy::trig::TrigPolicy>::atan_impl(self, mode)
260 }
261
262 #[inline]
263 #[must_use]
264 pub fn atan_approx(self, working_digits: u32) -> Self {
265 self.atan_approx_with(working_digits, crate::support::rounding::DEFAULT_ROUNDING_MODE)
266 }
267
268 #[inline]
269 #[must_use]
270 pub fn atan_approx_with(self, working_digits: u32, mode: crate::support::rounding::RoundingMode) -> Self {
271 if working_digits == STRICT_GUARD {
272 return self.atan_strict_with(mode);
273 }
274 <Self as crate::policy::trig::TrigPolicy>::atan_with_impl(self, working_digits, mode)
275 }
276
277 #[inline]
279 #[must_use]
280 pub fn asin_strict(self) -> Self {
281 self.asin_strict_with(crate::support::rounding::DEFAULT_ROUNDING_MODE)
282 }
283
284 #[inline]
285 #[must_use]
286 pub fn asin_strict_with(self, mode: crate::support::rounding::RoundingMode) -> Self {
287 <Self as crate::policy::trig::TrigPolicy>::asin_impl(self, mode)
288 }
289
290 #[inline]
291 #[must_use]
292 pub fn asin_approx(self, working_digits: u32) -> Self {
293 self.asin_approx_with(working_digits, crate::support::rounding::DEFAULT_ROUNDING_MODE)
294 }
295
296 #[inline]
297 #[must_use]
298 pub fn asin_approx_with(self, working_digits: u32, mode: crate::support::rounding::RoundingMode) -> Self {
299 if working_digits == STRICT_GUARD {
300 return self.asin_strict_with(mode);
301 }
302 <Self as crate::policy::trig::TrigPolicy>::asin_with_impl(self, working_digits, mode)
303 }
304
305 #[inline]
307 #[must_use]
308 pub fn acos_strict(self) -> Self {
309 self.acos_strict_with(crate::support::rounding::DEFAULT_ROUNDING_MODE)
310 }
311
312 #[inline]
313 #[must_use]
314 pub fn acos_strict_with(self, mode: crate::support::rounding::RoundingMode) -> Self {
315 <Self as crate::policy::trig::TrigPolicy>::acos_impl(self, mode)
316 }
317
318 #[inline]
319 #[must_use]
320 pub fn acos_approx(self, working_digits: u32) -> Self {
321 self.acos_approx_with(working_digits, crate::support::rounding::DEFAULT_ROUNDING_MODE)
322 }
323
324 #[inline]
325 #[must_use]
326 pub fn acos_approx_with(self, working_digits: u32, mode: crate::support::rounding::RoundingMode) -> Self {
327 if working_digits == STRICT_GUARD {
328 return self.acos_strict_with(mode);
329 }
330 <Self as crate::policy::trig::TrigPolicy>::acos_with_impl(self, working_digits, mode)
331 }
332
333 #[inline]
335 #[must_use]
336 pub fn atan2_strict(self, other: Self) -> Self {
337 self.atan2_strict_with(other, crate::support::rounding::DEFAULT_ROUNDING_MODE)
338 }
339
340 #[inline]
341 #[must_use]
342 pub fn atan2_strict_with(self, other: Self, mode: crate::support::rounding::RoundingMode) -> Self {
343 <Self as crate::policy::trig::TrigPolicy>::atan2_impl(self, other, mode)
344 }
345
346 #[inline]
347 #[must_use]
348 pub fn atan2_approx(self, other: Self, working_digits: u32) -> Self {
349 self.atan2_approx_with(other, working_digits, crate::support::rounding::DEFAULT_ROUNDING_MODE)
350 }
351
352 #[inline]
353 #[must_use]
354 pub fn atan2_approx_with(self, other: Self, working_digits: u32, mode: crate::support::rounding::RoundingMode) -> Self {
355 if working_digits == STRICT_GUARD {
356 return self.atan2_strict_with(other, mode);
357 }
358 <Self as crate::policy::trig::TrigPolicy>::atan2_with_impl(self, other, working_digits, mode)
359 }
360
361 #[inline]
365 #[must_use]
366 pub fn sinh_strict(self) -> Self {
367 self.sinh_strict_with(crate::support::rounding::DEFAULT_ROUNDING_MODE)
368 }
369
370 #[inline]
371 #[must_use]
372 pub fn sinh_strict_with(self, mode: crate::support::rounding::RoundingMode) -> Self {
373 <Self as crate::policy::trig::TrigPolicy>::sinh_impl(self, mode)
374 }
375
376 #[inline]
377 #[must_use]
378 pub fn sinh_approx(self, working_digits: u32) -> Self {
379 self.sinh_approx_with(working_digits, crate::support::rounding::DEFAULT_ROUNDING_MODE)
380 }
381
382 #[inline]
383 #[must_use]
384 pub fn sinh_approx_with(self, working_digits: u32, mode: crate::support::rounding::RoundingMode) -> Self {
385 if working_digits == STRICT_GUARD {
386 return self.sinh_strict_with(mode);
387 }
388 <Self as crate::policy::trig::TrigPolicy>::sinh_with_impl(self, working_digits, mode)
389 }
390
391 #[inline]
393 #[must_use]
394 pub fn cosh_strict(self) -> Self {
395 self.cosh_strict_with(crate::support::rounding::DEFAULT_ROUNDING_MODE)
396 }
397
398 #[inline]
399 #[must_use]
400 pub fn cosh_strict_with(self, mode: crate::support::rounding::RoundingMode) -> Self {
401 <Self as crate::policy::trig::TrigPolicy>::cosh_impl(self, mode)
402 }
403
404 #[inline]
405 #[must_use]
406 pub fn cosh_approx(self, working_digits: u32) -> Self {
407 self.cosh_approx_with(working_digits, crate::support::rounding::DEFAULT_ROUNDING_MODE)
408 }
409
410 #[inline]
411 #[must_use]
412 pub fn cosh_approx_with(self, working_digits: u32, mode: crate::support::rounding::RoundingMode) -> Self {
413 if working_digits == STRICT_GUARD {
414 return self.cosh_strict_with(mode);
415 }
416 <Self as crate::policy::trig::TrigPolicy>::cosh_with_impl(self, working_digits, mode)
417 }
418
419 #[inline]
421 #[must_use]
422 pub fn tanh_strict(self) -> Self {
423 self.tanh_strict_with(crate::support::rounding::DEFAULT_ROUNDING_MODE)
424 }
425
426 #[inline]
427 #[must_use]
428 pub fn tanh_strict_with(self, mode: crate::support::rounding::RoundingMode) -> Self {
429 <Self as crate::policy::trig::TrigPolicy>::tanh_impl(self, mode)
430 }
431
432 #[inline]
433 #[must_use]
434 pub fn tanh_approx(self, working_digits: u32) -> Self {
435 self.tanh_approx_with(working_digits, crate::support::rounding::DEFAULT_ROUNDING_MODE)
436 }
437
438 #[inline]
439 #[must_use]
440 pub fn tanh_approx_with(self, working_digits: u32, mode: crate::support::rounding::RoundingMode) -> Self {
441 if working_digits == STRICT_GUARD {
442 return self.tanh_strict_with(mode);
443 }
444 <Self as crate::policy::trig::TrigPolicy>::tanh_with_impl(self, working_digits, mode)
445 }
446
447 #[inline]
449 #[must_use]
450 pub fn asinh_strict(self) -> Self {
451 self.asinh_strict_with(crate::support::rounding::DEFAULT_ROUNDING_MODE)
452 }
453
454 #[inline]
455 #[must_use]
456 pub fn asinh_strict_with(self, mode: crate::support::rounding::RoundingMode) -> Self {
457 <Self as crate::policy::trig::TrigPolicy>::asinh_impl(self, mode)
458 }
459
460 #[inline]
461 #[must_use]
462 pub fn asinh_approx(self, working_digits: u32) -> Self {
463 self.asinh_approx_with(working_digits, crate::support::rounding::DEFAULT_ROUNDING_MODE)
464 }
465
466 #[inline]
467 #[must_use]
468 pub fn asinh_approx_with(self, working_digits: u32, mode: crate::support::rounding::RoundingMode) -> Self {
469 if working_digits == STRICT_GUARD {
470 return self.asinh_strict_with(mode);
471 }
472 <Self as crate::policy::trig::TrigPolicy>::asinh_with_impl(self, working_digits, mode)
473 }
474
475 #[inline]
477 #[must_use]
478 pub fn acosh_strict(self) -> Self {
479 self.acosh_strict_with(crate::support::rounding::DEFAULT_ROUNDING_MODE)
480 }
481
482 #[inline]
483 #[must_use]
484 pub fn acosh_strict_with(self, mode: crate::support::rounding::RoundingMode) -> Self {
485 <Self as crate::policy::trig::TrigPolicy>::acosh_impl(self, mode)
486 }
487
488 #[inline]
489 #[must_use]
490 pub fn acosh_approx(self, working_digits: u32) -> Self {
491 self.acosh_approx_with(working_digits, crate::support::rounding::DEFAULT_ROUNDING_MODE)
492 }
493
494 #[inline]
495 #[must_use]
496 pub fn acosh_approx_with(self, working_digits: u32, mode: crate::support::rounding::RoundingMode) -> Self {
497 if working_digits == STRICT_GUARD {
498 return self.acosh_strict_with(mode);
499 }
500 <Self as crate::policy::trig::TrigPolicy>::acosh_with_impl(self, working_digits, mode)
501 }
502
503 #[inline]
505 #[must_use]
506 pub fn atanh_strict(self) -> Self {
507 self.atanh_strict_with(crate::support::rounding::DEFAULT_ROUNDING_MODE)
508 }
509
510 #[inline]
511 #[must_use]
512 pub fn atanh_strict_with(self, mode: crate::support::rounding::RoundingMode) -> Self {
513 <Self as crate::policy::trig::TrigPolicy>::atanh_impl(self, mode)
514 }
515
516 #[inline]
517 #[must_use]
518 pub fn atanh_approx(self, working_digits: u32) -> Self {
519 self.atanh_approx_with(working_digits, crate::support::rounding::DEFAULT_ROUNDING_MODE)
520 }
521
522 #[inline]
523 #[must_use]
524 pub fn atanh_approx_with(self, working_digits: u32, mode: crate::support::rounding::RoundingMode) -> Self {
525 if working_digits == STRICT_GUARD {
526 return self.atanh_strict_with(mode);
527 }
528 <Self as crate::policy::trig::TrigPolicy>::atanh_with_impl(self, working_digits, mode)
529 }
530
531 #[inline]
535 #[must_use]
536 pub fn to_degrees_strict(self) -> Self {
537 self.to_degrees_strict_with(crate::support::rounding::DEFAULT_ROUNDING_MODE)
538 }
539
540 #[inline]
541 #[must_use]
542 pub fn to_degrees_strict_with(self, mode: crate::support::rounding::RoundingMode) -> Self {
543 <Self as crate::policy::trig::TrigPolicy>::to_degrees_impl(self, mode)
544 }
545
546 #[inline]
547 #[must_use]
548 pub fn to_degrees_approx(self, working_digits: u32) -> Self {
549 self.to_degrees_approx_with(working_digits, crate::support::rounding::DEFAULT_ROUNDING_MODE)
550 }
551
552 #[inline]
553 #[must_use]
554 pub fn to_degrees_approx_with(self, working_digits: u32, mode: crate::support::rounding::RoundingMode) -> Self {
555 if working_digits == STRICT_GUARD {
556 return self.to_degrees_strict_with(mode);
557 }
558 <Self as crate::policy::trig::TrigPolicy>::to_degrees_with_impl(self, working_digits, mode)
559 }
560
561 #[inline]
563 #[must_use]
564 pub fn to_radians_strict(self) -> Self {
565 self.to_radians_strict_with(crate::support::rounding::DEFAULT_ROUNDING_MODE)
566 }
567
568 #[inline]
569 #[must_use]
570 pub fn to_radians_strict_with(self, mode: crate::support::rounding::RoundingMode) -> Self {
571 <Self as crate::policy::trig::TrigPolicy>::to_radians_impl(self, mode)
572 }
573
574 #[inline]
575 #[must_use]
576 pub fn to_radians_approx(self, working_digits: u32) -> Self {
577 self.to_radians_approx_with(working_digits, crate::support::rounding::DEFAULT_ROUNDING_MODE)
578 }
579
580 #[inline]
581 #[must_use]
582 pub fn to_radians_approx_with(self, working_digits: u32, mode: crate::support::rounding::RoundingMode) -> Self {
583 if working_digits == STRICT_GUARD {
584 return self.to_radians_strict_with(mode);
585 }
586 <Self as crate::policy::trig::TrigPolicy>::to_radians_with_impl(self, working_digits, mode)
587 }
588}
589
590#[cfg(test)]
591mod tests {
592 use crate::types::consts::DecimalConstants;
593 use crate::types::widths::D38s12;
594
595 const TWO_LSB: i128 = 2;
601
602 const FOUR_LSB: i128 = 4;
604
605 const ANGLE_TOLERANCE_LSB: i128 = 32;
608
609 fn within_lsb(actual: D38s12, expected: D38s12, lsb: i128) -> bool {
610 let diff = (actual.to_bits() - expected.to_bits()).abs();
611 diff <= lsb
612 }
613
614 #[cfg(all(feature = "strict", not(feature = "fast")))]
619 #[test]
620 fn strict_trig_family_matches_f64() {
621 use crate::types::widths::D38;
622 macro_rules! check {
623 ($name:literal, $raw:expr, $strict:expr, $f64expr:expr) => {{
624 let strict: i128 = $strict;
625 let v = $raw as f64 / 1e9;
626 let reference = ($f64expr(v) * 1e9).round() as i128;
627 assert!(
628 (strict - reference).abs() <= 2,
629 concat!($name, "({}) = {}, f64 reference {}"),
630 $raw,
631 strict,
632 reference
633 );
634 }};
635 }
636 for &raw in &[
637 -7_000_000_000_i128, -1_000_000_000, -100_000_000, 1,
638 500_000_000, 1_000_000_000, 1_570_796_327, 3_000_000_000,
639 6_283_185_307, 12_000_000_000,
640 ] {
641 let x = D38::<9>::from_bits(raw);
642 check!("sin", raw, x.sin_strict().to_bits(), f64::sin);
643 check!("cos", raw, x.cos_strict().to_bits(), f64::cos);
644 check!("atan", raw, x.atan_strict().to_bits(), f64::atan);
645 check!("sinh", raw, x.sinh_strict().to_bits(), f64::sinh);
646 check!("cosh", raw, x.cosh_strict().to_bits(), f64::cosh);
647 check!("tanh", raw, x.tanh_strict().to_bits(), f64::tanh);
648 check!("asinh", raw, x.asinh_strict().to_bits(), f64::asinh);
649 }
650 for &raw in &[
651 -1_000_000_000_i128, -700_000_000, -100_000_000, 0,
652 250_000_000, 500_000_000, 999_999_999,
653 ] {
654 let x = D38::<9>::from_bits(raw);
655 check!("asin", raw, x.asin_strict().to_bits(), f64::asin);
656 check!("acos", raw, x.acos_strict().to_bits(), f64::acos);
657 }
658 for &raw in &[-900_000_000_i128, -300_000_000, 1, 300_000_000, 900_000_000] {
659 let x = D38::<9>::from_bits(raw);
660 check!("atanh", raw, x.atanh_strict().to_bits(), f64::atanh);
661 }
662 for &raw in &[1_000_000_000_i128, 1_500_000_000, 3_000_000_000, 50_000_000_000] {
663 let x = D38::<9>::from_bits(raw);
664 check!("acosh", raw, x.acosh_strict().to_bits(), f64::acosh);
665 }
666 for &raw in &[-1_000_000_000_i128, 1, 500_000_000, 1_000_000_000, 1_400_000_000] {
667 let x = D38::<9>::from_bits(raw);
668 check!("tan", raw, x.tan_strict().to_bits(), f64::tan);
669 }
670 }
671
672 #[test]
674 fn sin_zero_is_zero() {
675 assert_eq!(D38s12::ZERO.sin(), D38s12::ZERO);
676 }
677
678 #[cfg(all(feature = "strict", not(feature = "fast")))]
682 #[test]
683 fn sin_one_correct_past_63_digit_pi_window() {
684 use crate::types::widths::D38;
685 let expected_35: i128 = 84_147_098_480_789_650_665_250_232_163_029_900;
686 let expected_37: i128 =
687 8_414_709_848_078_965_066_525_023_216_302_989_996;
688
689 let got_35 = D38::<35>::ONE.sin_strict().to_bits();
690 assert!(
691 (got_35 - expected_35).abs() <= 1,
692 "sin(1) @ D38<35>: got {got_35}, expected {expected_35}"
693 );
694
695 let got_37 = D38::<37>::ONE.sin_strict().to_bits();
696 assert!(
697 (got_37 - expected_37).abs() <= 1,
698 "sin(1) @ D38<37>: got {got_37}, expected {expected_37}"
699 );
700 }
701
702 #[test]
704 fn cos_zero_is_one() {
705 assert_eq!(D38s12::ZERO.cos(), D38s12::ONE);
706 }
707
708 #[test]
710 fn tan_zero_is_zero() {
711 assert_eq!(D38s12::ZERO.tan(), D38s12::ZERO);
712 }
713
714 #[test]
716 fn sin_squared_plus_cos_squared_is_one() {
717 for raw in [
718 1_234_567_890_123_i128,
719 -2_345_678_901_234_i128,
720 500_000_000_000_i128,
721 -500_000_000_000_i128,
722 4_567_891_234_567_i128,
723 ] {
724 let x = D38s12::from_bits(raw);
725 let s = x.sin();
726 let c = x.cos();
727 let sum = (s * s) + (c * c);
728 assert!(
729 within_lsb(sum, D38s12::ONE, FOUR_LSB),
730 "sin^2 + cos^2 != 1 for raw={raw}: got bits {} (delta {})",
731 sum.to_bits(),
732 (sum.to_bits() - D38s12::ONE.to_bits()).abs(),
733 );
734 }
735 }
736
737 #[test]
740 fn asin_zero_is_zero() {
741 assert_eq!(D38s12::ZERO.asin(), D38s12::ZERO);
742 }
743
744 #[test]
745 fn acos_one_is_zero() {
746 assert_eq!(D38s12::ONE.acos(), D38s12::ZERO);
747 }
748
749 #[test]
750 fn acos_zero_is_half_pi() {
751 let result = D38s12::ZERO.acos();
752 assert!(
753 within_lsb(result, D38s12::half_pi(), FOUR_LSB),
754 "acos(0) bits {}, half_pi bits {}",
755 result.to_bits(),
756 D38s12::half_pi().to_bits(),
757 );
758 }
759
760 #[test]
761 fn atan_zero_is_zero() {
762 assert_eq!(D38s12::ZERO.atan(), D38s12::ZERO);
763 }
764
765 #[test]
766 fn asin_of_sin_round_trip() {
767 for raw in [
768 123_456_789_012_i128,
769 -123_456_789_012_i128,
770 456_789_012_345_i128,
771 -456_789_012_345_i128,
772 1_234_567_890_123_i128,
773 -1_234_567_890_123_i128,
774 ] {
775 let x = D38s12::from_bits(raw);
776 let recovered = x.sin().asin();
777 assert!(
778 within_lsb(recovered, x, FOUR_LSB),
779 "asin(sin(x)) != x for raw={raw}: got bits {} (delta {})",
780 recovered.to_bits(),
781 (recovered.to_bits() - x.to_bits()).abs(),
782 );
783 }
784 }
785
786 #[test]
789 fn atan2_first_quadrant_diagonal() {
790 let one = D38s12::ONE;
791 let result = one.atan2(one);
792 assert!(
793 within_lsb(result, D38s12::quarter_pi(), TWO_LSB),
794 "atan2(1, 1) bits {}, quarter_pi bits {}",
795 result.to_bits(),
796 D38s12::quarter_pi().to_bits(),
797 );
798 }
799
800 #[test]
801 fn atan2_third_quadrant_diagonal() {
802 let neg_one = -D38s12::ONE;
803 let result = neg_one.atan2(neg_one);
804 let three = D38s12::from_int(3);
805 let expected = -(D38s12::quarter_pi() * three);
806 assert!(
807 within_lsb(result, expected, TWO_LSB),
808 "atan2(-1, -1) bits {}, expected -3pi/4 bits {}",
809 result.to_bits(),
810 expected.to_bits(),
811 );
812 }
813
814 #[test]
815 fn atan2_second_quadrant_diagonal() {
816 let one = D38s12::ONE;
817 let neg_one = -D38s12::ONE;
818 let result = one.atan2(neg_one);
819 let three = D38s12::from_int(3);
820 let expected = D38s12::quarter_pi() * three;
821 assert!(
822 within_lsb(result, expected, TWO_LSB),
823 "atan2(1, -1) bits {}, expected 3pi/4 bits {}",
824 result.to_bits(),
825 expected.to_bits(),
826 );
827 }
828
829 #[test]
830 fn atan2_fourth_quadrant_diagonal() {
831 let one = D38s12::ONE;
832 let neg_one = -D38s12::ONE;
833 let result = neg_one.atan2(one);
834 let expected = -D38s12::quarter_pi();
835 assert!(
836 within_lsb(result, expected, TWO_LSB),
837 "atan2(-1, 1) bits {}, expected -pi/4 bits {}",
838 result.to_bits(),
839 expected.to_bits(),
840 );
841 }
842
843 #[test]
844 fn atan2_positive_x_axis_is_zero() {
845 let zero = D38s12::ZERO;
846 let one = D38s12::ONE;
847 assert_eq!(zero.atan2(one), D38s12::ZERO);
848 }
849
850 #[test]
853 fn sinh_zero_is_zero() {
854 assert_eq!(D38s12::ZERO.sinh(), D38s12::ZERO);
855 }
856
857 #[test]
858 fn cosh_zero_is_one() {
859 assert_eq!(D38s12::ZERO.cosh(), D38s12::ONE);
860 }
861
862 #[test]
863 fn tanh_zero_is_zero() {
864 assert_eq!(D38s12::ZERO.tanh(), D38s12::ZERO);
865 }
866
867 #[test]
868 fn asinh_zero_is_zero() {
869 assert_eq!(D38s12::ZERO.asinh(), D38s12::ZERO);
870 }
871
872 #[test]
873 fn acosh_one_is_zero() {
874 assert_eq!(D38s12::ONE.acosh(), D38s12::ZERO);
875 }
876
877 #[test]
878 fn atanh_zero_is_zero() {
879 assert_eq!(D38s12::ZERO.atanh(), D38s12::ZERO);
880 }
881
882 #[test]
883 fn cosh_squared_minus_sinh_squared_is_one() {
884 if !crate::support::rounding::DEFAULT_IS_HALF_TO_EVEN { return; }
885 for raw in [
886 500_000_000_000_i128,
887 -500_000_000_000_i128,
888 1_234_567_890_123_i128,
889 -1_234_567_890_123_i128,
890 2_500_000_000_000_i128,
891 ] {
892 let x = D38s12::from_bits(raw);
893 let ch = x.cosh();
894 let sh = x.sinh();
895 let diff = (ch * ch) - (sh * sh);
896 assert!(
897 within_lsb(diff, D38s12::ONE, FOUR_LSB),
898 "cosh^2 - sinh^2 != 1 for raw={raw}: got bits {} (delta {})",
899 diff.to_bits(),
900 (diff.to_bits() - D38s12::ONE.to_bits()).abs(),
901 );
902 }
903 }
904
905 #[test]
908 fn to_degrees_pi_is_180() {
909 if !crate::support::rounding::DEFAULT_IS_HALF_TO_EVEN { return; }
910 let pi = D38s12::pi();
911 let result = pi.to_degrees();
912 let expected = D38s12::from_int(180);
913 assert!(
914 within_lsb(result, expected, ANGLE_TOLERANCE_LSB),
915 "to_degrees(pi) bits {}, expected 180 bits {} (delta {})",
916 result.to_bits(),
917 expected.to_bits(),
918 (result.to_bits() - expected.to_bits()).abs(),
919 );
920 }
921
922 #[test]
923 fn to_radians_180_is_pi() {
924 let one_eighty = D38s12::from_int(180);
925 let result = one_eighty.to_radians();
926 let expected = D38s12::pi();
927 assert!(
928 within_lsb(result, expected, ANGLE_TOLERANCE_LSB),
929 "to_radians(180) bits {}, expected pi bits {} (delta {})",
930 result.to_bits(),
931 expected.to_bits(),
932 (result.to_bits() - expected.to_bits()).abs(),
933 );
934 }
935
936 #[test]
937 fn to_degrees_zero_is_zero() {
938 assert_eq!(D38s12::ZERO.to_degrees(), D38s12::ZERO);
939 }
940
941 #[test]
942 fn to_radians_zero_is_zero() {
943 assert_eq!(D38s12::ZERO.to_radians(), D38s12::ZERO);
944 }
945
946 #[test]
947 fn to_radians_to_degrees_round_trip() {
948 for raw in [
949 500_000_000_000_i128,
950 -500_000_000_000_i128,
951 1_234_567_890_123_i128,
952 -2_345_678_901_234_i128,
953 ] {
954 let x = D38s12::from_bits(raw);
955 let recovered = x.to_degrees().to_radians();
956 assert!(
957 within_lsb(recovered, x, FOUR_LSB),
958 "to_radians(to_degrees(x)) != x for raw={raw}: got bits {} (delta {})",
959 recovered.to_bits(),
960 (recovered.to_bits() - x.to_bits()).abs(),
961 );
962 }
963 }
964
965 #[test]
966 fn to_degrees_half_pi_is_90() {
967 if !crate::support::rounding::DEFAULT_IS_HALF_TO_EVEN { return; }
968 let result = D38s12::half_pi().to_degrees();
969 let expected = D38s12::from_int(90);
970 assert!(
971 within_lsb(result, expected, ANGLE_TOLERANCE_LSB),
972 "to_degrees(half_pi) bits {}, expected 90 bits {} (delta {})",
973 result.to_bits(),
974 expected.to_bits(),
975 (result.to_bits() - expected.to_bits()).abs(),
976 );
977 }
978
979 #[test]
980 fn to_degrees_quarter_pi_is_45() {
981 let result = D38s12::quarter_pi().to_degrees();
982 let expected = D38s12::from_int(45);
983 assert!(
984 within_lsb(result, expected, ANGLE_TOLERANCE_LSB),
985 "to_degrees(quarter_pi) bits {}, expected 45 bits {} (delta {})",
986 result.to_bits(),
987 expected.to_bits(),
988 (result.to_bits() - expected.to_bits()).abs(),
989 );
990 }
991
992 #[test]
995 fn tan_matches_sin_over_cos() {
996 for raw in [
997 500_000_000_000_i128,
998 -500_000_000_000_i128,
999 1_000_000_000_000_i128,
1000 -1_000_000_000_000_i128,
1001 123_456_789_012_i128,
1002 ] {
1003 let x = D38s12::from_bits(raw);
1004 let t = x.tan();
1005 let sc = x.sin() / x.cos();
1006 assert!(
1007 within_lsb(t, sc, FOUR_LSB),
1008 "tan(x) != sin/cos for raw={raw}: tan bits {}, sin/cos bits {}",
1009 t.to_bits(),
1010 sc.to_bits(),
1011 );
1012 }
1013 }
1014
1015 #[test]
1016 fn tanh_matches_sinh_over_cosh() {
1017 for raw in [
1018 500_000_000_000_i128,
1019 -500_000_000_000_i128,
1020 1_234_567_890_123_i128,
1021 -2_345_678_901_234_i128,
1022 ] {
1023 let x = D38s12::from_bits(raw);
1024 let t = x.tanh();
1025 let sc = x.sinh() / x.cosh();
1026 assert!(
1027 within_lsb(t, sc, FOUR_LSB),
1028 "tanh(x) != sinh/cosh for raw={raw}: tanh bits {}, sinh/cosh bits {}",
1029 t.to_bits(),
1030 sc.to_bits(),
1031 );
1032 }
1033 }
1034}