1use crate::core_type::D38;
59
60pub(crate) const STRICT_GUARD: u32 = 30;
76
77pub(crate) fn wide_ln2(w: u32) -> crate::d_w128_kernels::Fixed {
80 crate::d_w128_kernels::Fixed::from_decimal_split(
82 69_314_718_055_994_530_941_723_212_145_817_u128,
83 65_680_755_001_343_602_552_541_206_800_094_u128,
84 )
85 .rescale_down(64, w)
86}
87
88fn wide_ln10(w: u32) -> crate::d_w128_kernels::Fixed {
92 crate::d_w128_kernels::Fixed::from_decimal_split(
94 23_025_850_929_940_456_840_179_914_546_843_u128,
95 64_207_601_101_488_628_772_976_033_327_901_u128,
96 )
97 .rescale_down(63, w)
98}
99
100pub(crate) fn ln_fixed(v_w: crate::d_w128_kernels::Fixed, w: u32) -> crate::d_w128_kernels::Fixed {
108 use crate::d_w128_kernels::Fixed;
109 let one_w = Fixed { negative: false, mag: Fixed::pow10(w) };
110 let two_w = one_w.double();
111
112 let mut k: i32 = v_w.bit_length() as i32 - one_w.bit_length() as i32;
114 let m_w = loop {
115 let m = if k >= 0 {
116 v_w.shr(k as u32)
117 } else {
118 v_w.shl((-k) as u32)
119 };
120 if m.ge_mag(two_w) {
121 k += 1;
122 } else if !m.ge_mag(one_w) {
123 k -= 1;
124 } else {
125 break m;
126 }
127 };
128
129 let t = m_w.sub(one_w).div(m_w.add(one_w), w);
131 let t2 = t.mul(t, w);
132 let mut sum = t;
133 let mut term = t;
134 let mut j: u128 = 1;
135 loop {
136 term = term.mul(t2, w);
137 let contrib = term.div_small(2 * j + 1);
138 if contrib.is_zero() {
139 break;
140 }
141 sum = sum.add(contrib);
142 j += 1;
143 if j > 400 {
144 break;
145 }
146 }
147 let ln_m = sum.double();
148
149 let ln2 = wide_ln2(w);
150 let k_ln2 = if k >= 0 {
151 ln2.mul_u128(k as u128)
152 } else {
153 ln2.mul_u128((-k) as u128).neg()
154 };
155 k_ln2.add(ln_m)
156}
157
158pub(crate) fn exp_fixed(v_w: crate::d_w128_kernels::Fixed, w: u32) -> crate::d_w128_kernels::Fixed {
171 use crate::d_w128_kernels::Fixed;
172 let one_w = Fixed { negative: false, mag: Fixed::pow10(w) };
173 let ln2 = wide_ln2(w);
174
175 let k = v_w.div(ln2, w).round_to_nearest_int(w);
177 let k_ln2 = if k >= 0 {
178 ln2.mul_u128(k as u128)
179 } else {
180 ln2.mul_u128((-k) as u128).neg()
181 };
182 let s = v_w.sub(k_ln2);
183
184 let mut sum = one_w;
186 let mut term = one_w;
187 let mut n: u128 = 1;
188 loop {
189 term = term.mul(s, w).div_small(n);
190 if term.is_zero() {
191 break;
192 }
193 sum = sum.add(term);
194 n += 1;
195 if n > 400 {
196 break;
197 }
198 }
199
200 if k >= 0 {
202 let shift = k as u32;
203 assert!(sum.bit_length() + shift <= 256, "D38::exp: result overflows the representable range");
204 sum.shl(shift)
205 } else {
206 sum.shr((-k) as u32)
207 }
208}
209
210impl<const SCALE: u32> D38<SCALE> {
211 #[inline]
242 #[must_use]
243 pub fn ln_strict(self) -> Self {
244 self.ln_strict_with(crate::rounding::DEFAULT_ROUNDING_MODE)
245 }
246
247 #[inline]
249 #[must_use]
250 pub fn ln_strict_with(self, mode: crate::rounding::RoundingMode) -> Self {
251 use crate::d_w128_kernels::Fixed;
252 assert!(self.0 > 0, "D38::ln: argument must be positive");
253 let one_bits: i128 = 10_i128.pow(SCALE);
254 if self.0 == one_bits {
255 return Self::ZERO;
256 }
257 let delta = self.0 - one_bits;
258 let ln1p_band: i128 = 10_i128.pow(SCALE.saturating_sub((SCALE + 1) / 2));
259 if delta.abs() <= ln1p_band {
260 return Self::from_bits(delta);
261 }
262 let w = SCALE + STRICT_GUARD;
265 let v_w =
266 Fixed::from_u128_mag(self.0 as u128, false).mul_u128(10u128.pow(STRICT_GUARD));
267 let raw = ln_fixed(v_w, w)
268 .round_to_i128_with(w, SCALE, mode)
269 .expect("D38::ln: result out of range");
270 Self::from_bits(raw)
271 }
272
273 #[inline]
302 #[must_use]
303 pub fn ln_approx(self, working_digits: u32) -> Self {
304 self.ln_approx_with(working_digits, crate::rounding::DEFAULT_ROUNDING_MODE)
305 }
306
307 #[inline]
310 #[must_use]
311 pub fn ln_approx_with(self, working_digits: u32, mode: crate::rounding::RoundingMode) -> Self {
312 if working_digits == STRICT_GUARD {
314 return self.ln_strict_with(mode);
315 }
316 use crate::d_w128_kernels::Fixed;
317 assert!(self.0 > 0, "D38::ln: argument must be positive");
318 let one_bits: i128 = 10_i128.pow(SCALE);
319 if self.0 == one_bits {
320 return Self::ZERO;
321 }
322 let delta = self.0 - one_bits;
323 let ln1p_band: i128 = 10_i128.pow(SCALE.saturating_sub((SCALE + 1) / 2));
324 if delta.abs() <= ln1p_band {
325 return Self::from_bits(delta);
326 }
327 let w = SCALE + working_digits;
328 let v_w =
329 Fixed::from_u128_mag(self.0 as u128, false).mul_u128(10u128.pow(working_digits));
330 let raw = ln_fixed(v_w, w)
331 .round_to_i128_with(w, SCALE, mode)
332 .expect("D38::ln: result out of range");
333 Self::from_bits(raw)
334 }
335
336 #[cfg(all(feature = "strict", not(feature = "fast")))]
341 #[inline]
342 #[must_use]
343 pub fn ln(self) -> Self {
344 self.ln_strict()
345 }
346
347 #[inline]
359 #[must_use]
360 pub fn log_strict(self, base: Self) -> Self {
361 self.log_strict_with(base, crate::rounding::DEFAULT_ROUNDING_MODE)
362 }
363
364 #[inline]
366 #[must_use]
367 pub fn log_strict_with(self, base: Self, mode: crate::rounding::RoundingMode) -> Self {
368 use crate::d_w128_kernels::Fixed;
369 assert!(self.0 > 0, "D38::log: argument must be positive");
370 assert!(base.0 > 0, "D38::log: base must be positive");
371 let w = SCALE + STRICT_GUARD;
372 let pow = 10u128.pow(STRICT_GUARD);
373 let v_w = Fixed::from_u128_mag(self.0 as u128, false).mul_u128(pow);
374 let b_w = Fixed::from_u128_mag(base.0 as u128, false).mul_u128(pow);
375 let ln_b = ln_fixed(b_w, w);
376 assert!(!ln_b.is_zero(), "D38::log: base must not equal 1 (ln(1) is zero)");
377 let raw = ln_fixed(v_w, w)
378 .div(ln_b, w)
379 .round_to_i128_with(w, SCALE, mode)
380 .expect("D38::log: result out of range");
381 Self::from_bits(raw)
382 }
383
384 #[inline]
386 #[must_use]
387 pub fn log_approx(self, base: Self, working_digits: u32) -> Self {
388 self.log_approx_with(base, working_digits, crate::rounding::DEFAULT_ROUNDING_MODE)
389 }
390
391 #[inline]
393 #[must_use]
394 pub fn log_approx_with(self, base: Self, working_digits: u32, mode: crate::rounding::RoundingMode) -> Self {
395 if working_digits == STRICT_GUARD {
396 return self.log_strict_with(base, mode);
397 }
398 use crate::d_w128_kernels::Fixed;
399 assert!(self.0 > 0, "D38::log: argument must be positive");
400 assert!(base.0 > 0, "D38::log: base must be positive");
401 let w = SCALE + working_digits;
402 let pow = 10u128.pow(working_digits);
403 let v_w = Fixed::from_u128_mag(self.0 as u128, false).mul_u128(pow);
404 let b_w = Fixed::from_u128_mag(base.0 as u128, false).mul_u128(pow);
405 let ln_b = ln_fixed(b_w, w);
406 assert!(!ln_b.is_zero(), "D38::log: base must not equal 1 (ln(1) is zero)");
407 let raw = ln_fixed(v_w, w)
408 .div(ln_b, w)
409 .round_to_i128_with(w, SCALE, mode)
410 .expect("D38::log: result out of range");
411 Self::from_bits(raw)
412 }
413
414 #[cfg(all(feature = "strict", not(feature = "fast")))]
419 #[inline]
420 #[must_use]
421 pub fn log(self, base: Self) -> Self {
422 self.log_strict(base)
423 }
424
425 #[inline]
435 #[must_use]
436 pub fn log2_strict(self) -> Self {
437 self.log2_strict_with(crate::rounding::DEFAULT_ROUNDING_MODE)
438 }
439
440 #[inline]
442 #[must_use]
443 pub fn log2_strict_with(self, mode: crate::rounding::RoundingMode) -> Self {
444 use crate::d_w128_kernels::Fixed;
445 assert!(self.0 > 0, "D38::log2: argument must be positive");
446 let w = SCALE + STRICT_GUARD;
447 let v_w =
448 Fixed::from_u128_mag(self.0 as u128, false).mul_u128(10u128.pow(STRICT_GUARD));
449 let raw = ln_fixed(v_w, w)
450 .div(wide_ln2(w), w)
451 .round_to_i128_with(w, SCALE, mode)
452 .expect("D38::log2: result out of range");
453 Self::from_bits(raw)
454 }
455
456 #[inline]
458 #[must_use]
459 pub fn log2_approx(self, working_digits: u32) -> Self {
460 self.log2_approx_with(working_digits, crate::rounding::DEFAULT_ROUNDING_MODE)
461 }
462
463 #[inline]
465 #[must_use]
466 pub fn log2_approx_with(self, working_digits: u32, mode: crate::rounding::RoundingMode) -> Self {
467 if working_digits == STRICT_GUARD {
468 return self.log2_strict_with(mode);
469 }
470 use crate::d_w128_kernels::Fixed;
471 assert!(self.0 > 0, "D38::log2: argument must be positive");
472 let w = SCALE + working_digits;
473 let v_w =
474 Fixed::from_u128_mag(self.0 as u128, false).mul_u128(10u128.pow(working_digits));
475 let raw = ln_fixed(v_w, w)
476 .div(wide_ln2(w), w)
477 .round_to_i128_with(w, SCALE, mode)
478 .expect("D38::log2: result out of range");
479 Self::from_bits(raw)
480 }
481
482 #[cfg(all(feature = "strict", not(feature = "fast")))]
487 #[inline]
488 #[must_use]
489 pub fn log2(self) -> Self {
490 self.log2_strict()
491 }
492
493 #[inline]
503 #[must_use]
504 pub fn log10_strict(self) -> Self {
505 self.log10_strict_with(crate::rounding::DEFAULT_ROUNDING_MODE)
506 }
507
508 #[inline]
510 #[must_use]
511 pub fn log10_strict_with(self, mode: crate::rounding::RoundingMode) -> Self {
512 use crate::d_w128_kernels::Fixed;
513 assert!(self.0 > 0, "D38::log10: argument must be positive");
514 let w = SCALE + STRICT_GUARD;
515 let v_w =
516 Fixed::from_u128_mag(self.0 as u128, false).mul_u128(10u128.pow(STRICT_GUARD));
517 let raw = ln_fixed(v_w, w)
518 .div(wide_ln10(w), w)
519 .round_to_i128_with(w, SCALE, mode)
520 .expect("D38::log10: result out of range");
521 Self::from_bits(raw)
522 }
523
524 #[inline]
526 #[must_use]
527 pub fn log10_approx(self, working_digits: u32) -> Self {
528 self.log10_approx_with(working_digits, crate::rounding::DEFAULT_ROUNDING_MODE)
529 }
530
531 #[inline]
533 #[must_use]
534 pub fn log10_approx_with(self, working_digits: u32, mode: crate::rounding::RoundingMode) -> Self {
535 if working_digits == STRICT_GUARD {
536 return self.log10_strict_with(mode);
537 }
538 use crate::d_w128_kernels::Fixed;
539 assert!(self.0 > 0, "D38::log10: argument must be positive");
540 let w = SCALE + working_digits;
541 let v_w =
542 Fixed::from_u128_mag(self.0 as u128, false).mul_u128(10u128.pow(working_digits));
543 let raw = ln_fixed(v_w, w)
544 .div(wide_ln10(w), w)
545 .round_to_i128_with(w, SCALE, mode)
546 .expect("D38::log10: result out of range");
547 Self::from_bits(raw)
548 }
549
550 #[cfg(all(feature = "strict", not(feature = "fast")))]
555 #[inline]
556 #[must_use]
557 pub fn log10(self) -> Self {
558 self.log10_strict()
559 }
560
561 #[inline]
586 #[must_use]
587 pub fn exp_strict(self) -> Self {
588 self.exp_strict_with(crate::rounding::DEFAULT_ROUNDING_MODE)
589 }
590
591 #[inline]
593 #[must_use]
594 pub fn exp_strict_with(self, mode: crate::rounding::RoundingMode) -> Self {
595 use crate::d_w128_kernels::Fixed;
596 if self.0 == 0 {
597 return Self::ONE;
598 }
599 let w = SCALE + STRICT_GUARD;
600 let negative_input = self.0 < 0;
601 let v_w = Fixed::from_u128_mag(self.0.unsigned_abs(), false)
602 .mul_u128(10u128.pow(STRICT_GUARD));
603 let v_w = if negative_input { v_w.neg() } else { v_w };
604 let raw = exp_fixed(v_w, w)
605 .round_to_i128_with(w, SCALE, mode)
606 .expect("D38::exp: result overflows the representable range");
607 Self::from_bits(raw)
608 }
609
610 #[inline]
612 #[must_use]
613 pub fn exp_approx(self, working_digits: u32) -> Self {
614 self.exp_approx_with(working_digits, crate::rounding::DEFAULT_ROUNDING_MODE)
615 }
616
617 #[inline]
619 #[must_use]
620 pub fn exp_approx_with(self, working_digits: u32, mode: crate::rounding::RoundingMode) -> Self {
621 if working_digits == STRICT_GUARD {
622 return self.exp_strict_with(mode);
623 }
624 use crate::d_w128_kernels::Fixed;
625 if self.0 == 0 {
626 return Self::ONE;
627 }
628 let w = SCALE + working_digits;
629 let negative_input = self.0 < 0;
630 let v_w = Fixed::from_u128_mag(self.0.unsigned_abs(), false)
631 .mul_u128(10u128.pow(working_digits));
632 let v_w = if negative_input { v_w.neg() } else { v_w };
633 let raw = exp_fixed(v_w, w)
634 .round_to_i128_with(w, SCALE, mode)
635 .expect("D38::exp: result overflows the representable range");
636 Self::from_bits(raw)
637 }
638
639 #[cfg(all(feature = "strict", not(feature = "fast")))]
644 #[inline]
645 #[must_use]
646 pub fn exp(self) -> Self {
647 self.exp_strict()
648 }
649
650 #[inline]
661 #[must_use]
662 pub fn exp2_strict(self) -> Self {
663 self.exp2_strict_with(crate::rounding::DEFAULT_ROUNDING_MODE)
664 }
665
666 #[inline]
668 #[must_use]
669 pub fn exp2_strict_with(self, mode: crate::rounding::RoundingMode) -> Self {
670 use crate::d_w128_kernels::Fixed;
671 if self.0 == 0 {
672 return Self::ONE;
673 }
674 let w = SCALE + STRICT_GUARD;
675 let negative_input = self.0 < 0;
676 let v_w = Fixed::from_u128_mag(self.0.unsigned_abs(), false)
677 .mul_u128(10u128.pow(STRICT_GUARD));
678 let v_w = if negative_input { v_w.neg() } else { v_w };
679 let arg_w = v_w.mul(wide_ln2(w), w);
680 let raw = exp_fixed(arg_w, w)
681 .round_to_i128_with(w, SCALE, mode)
682 .expect("D38::exp2: result overflows the representable range");
683 Self::from_bits(raw)
684 }
685
686 #[inline]
688 #[must_use]
689 pub fn exp2_approx(self, working_digits: u32) -> Self {
690 self.exp2_approx_with(working_digits, crate::rounding::DEFAULT_ROUNDING_MODE)
691 }
692
693 #[inline]
695 #[must_use]
696 pub fn exp2_approx_with(self, working_digits: u32, mode: crate::rounding::RoundingMode) -> Self {
697 if working_digits == STRICT_GUARD {
698 return self.exp2_strict_with(mode);
699 }
700 use crate::d_w128_kernels::Fixed;
701 if self.0 == 0 {
702 return Self::ONE;
703 }
704 let w = SCALE + working_digits;
705 let negative_input = self.0 < 0;
706 let v_w = Fixed::from_u128_mag(self.0.unsigned_abs(), false)
707 .mul_u128(10u128.pow(working_digits));
708 let v_w = if negative_input { v_w.neg() } else { v_w };
709 let arg_w = v_w.mul(wide_ln2(w), w);
710 let raw = exp_fixed(arg_w, w)
711 .round_to_i128_with(w, SCALE, mode)
712 .expect("D38::exp2: result overflows the representable range");
713 Self::from_bits(raw)
714 }
715
716 #[cfg(all(feature = "strict", not(feature = "fast")))]
721 #[inline]
722 #[must_use]
723 pub fn exp2(self) -> Self {
724 self.exp2_strict()
725 }
726}
727
728
729
730#[cfg(all(test, feature = "strict", not(feature = "fast")))]
731mod strict_tests {
732 use crate::core_type::D38s12;
733
734 const STRICT_TOLERANCE_LSB: i128 = 2;
738
739 fn within(actual: D38s12, expected_bits: i128, tolerance: i128) -> bool {
740 (actual.to_bits() - expected_bits).abs() <= tolerance
741 }
742
743 #[test]
745 fn ln_of_one_is_zero() {
746 assert_eq!(D38s12::ONE.ln(), D38s12::ZERO);
747 }
748
749 #[test]
754 fn ln_strict_is_correctly_rounded_vs_f64() {
755 use crate::core_type::D38;
756 fn check(raw: i128) {
760 let x = D38::<9>::from_bits(raw);
761 let strict = x.ln_strict().to_bits();
762 let reference = {
763 let v = raw as f64 / 1e9;
764 (v.ln() * 1e9).round() as i128
765 };
766 assert!(
767 (strict - reference).abs() <= 1,
768 "ln_strict({raw}) = {strict}, f64 reference {reference}"
769 );
770 }
771 for &raw in &[
772 1,
773 500_000_000, 1_000_000_000, 1_500_000_000, 2_000_000_000, 2_718_281_828, 10_000_000_000, 123_456_789_012_345, 999_999_999_999_999_999,i64::MAX as i128,
782 ] {
783 check(raw);
784 }
785 }
786
787 #[test]
792 fn strict_log_exp_family_matches_f64() {
793 use crate::core_type::D38;
794 fn check_exp(raw: i128) {
795 let x = D38::<9>::from_bits(raw);
796 let strict = x.exp_strict().to_bits();
797 let reference = ((raw as f64 / 1e9).exp() * 1e9).round() as i128;
798 assert!(
799 (strict - reference).abs() <= 1,
800 "exp_strict({raw}) = {strict}, f64 reference {reference}"
801 );
802 }
803 fn check_log2(raw: i128) {
804 let x = D38::<9>::from_bits(raw);
805 let strict = x.log2_strict().to_bits();
806 let reference = ((raw as f64 / 1e9).log2() * 1e9).round() as i128;
807 assert!(
808 (strict - reference).abs() <= 1,
809 "log2_strict({raw}) = {strict}, f64 reference {reference}"
810 );
811 }
812 fn check_log10(raw: i128) {
813 let x = D38::<9>::from_bits(raw);
814 let strict = x.log10_strict().to_bits();
815 let reference = ((raw as f64 / 1e9).log10() * 1e9).round() as i128;
816 assert!(
817 (strict - reference).abs() <= 1,
818 "log10_strict({raw}) = {strict}, f64 reference {reference}"
819 );
820 }
821 for &raw in &[
823 -5_000_000_000, -1_000_000_000, -500_000_000, 1, 500_000_000,
824 1_000_000_000, 2_000_000_000, 5_000_000_000, 10_000_000_000,
825 ] {
826 check_exp(raw);
827 }
828 for &raw in &[
830 1, 500_000_000, 1_000_000_000, 2_000_000_000, 8_000_000_000,
831 10_000_000_000, 123_456_789_012_345, i64::MAX as i128,
832 ] {
833 check_log2(raw);
834 check_log10(raw);
835 }
836 }
837
838 #[test]
840 fn strict_exp2_at_integers() {
841 use crate::core_type::D38;
842 for k in 0_i128..=12 {
843 let x = D38::<12>::from_bits(k * 10i128.pow(12));
844 let got = x.exp2_strict().to_bits();
845 let expected = (1i128 << k) * 10i128.pow(12);
846 assert_eq!(got, expected, "2^{k}");
848 }
849 }
850
851 #[test]
854 fn ln_strict_of_powers_of_two() {
855 use crate::core_type::D38;
856 let ln2_s18: i128 = 693_147_180_559_945_309;
859 for k in 1_i128..=20 {
860 let x = D38::<18>::from_bits((1i128 << k) * 10i128.pow(18));
861 let got = x.ln_strict().to_bits();
862 let expected = k * ln2_s18;
863 let tol = k / 2 + 2;
867 assert!(
868 (got - expected).abs() <= tol,
869 "ln(2^{k}) = {got}, expected ≈ {expected}"
870 );
871 }
872 }
873
874 #[test]
876 fn ln_of_two_close_to_canonical() {
877 let two = D38s12::from_bits(2_000_000_000_000);
878 let result = two.ln();
879 assert!(
881 within(result, 693_147_180_560, STRICT_TOLERANCE_LSB),
882 "ln(2) bits = {}",
883 result.to_bits()
884 );
885 }
886
887 #[test]
889 fn ln_of_e_close_to_one() {
890 let e_at_s12 = D38s12::from_bits(2_718_281_828_459);
892 let result = e_at_s12.ln();
893 assert!(
895 within(result, 1_000_000_000_000, STRICT_TOLERANCE_LSB),
896 "ln(e) bits = {}, expected ~1_000_000_000_000",
897 result.to_bits()
898 );
899 }
900
901 #[test]
903 fn ln_of_ten_close_to_canonical() {
904 let ten = D38s12::from_bits(10_000_000_000_000);
905 let result = ten.ln();
906 assert!(
907 within(result, 2_302_585_092_994, STRICT_TOLERANCE_LSB),
908 "ln(10) bits = {}, expected ~2_302_585_092_994",
909 result.to_bits()
910 );
911 }
912
913 #[test]
915 fn ln_above_one_is_positive() {
916 let v = D38s12::from_bits(1_500_000_000_000); let result = v.ln();
918 assert!(result.to_bits() > 0);
919 }
920
921 #[test]
923 fn ln_below_one_is_negative() {
924 let v = D38s12::from_bits(500_000_000_000); let result = v.ln();
926 assert!(result.to_bits() < 0);
927 assert!(
929 within(result, -693_147_180_560, STRICT_TOLERANCE_LSB),
930 "ln(0.5) bits = {}, expected ~-693_147_180_560",
931 result.to_bits()
932 );
933 }
934
935 #[test]
936 #[should_panic(expected = "argument must be positive")]
937 fn ln_of_zero_panics() {
938 let _ = D38s12::ZERO.ln();
939 }
940
941 #[test]
942 #[should_panic(expected = "argument must be positive")]
943 fn ln_of_negative_panics() {
944 let neg = D38s12::from_bits(-1_000_000_000_000);
945 let _ = neg.ln();
946 }
947
948 const DERIVED_LOG_TOLERANCE_LSB: i128 = 20;
951
952 #[test]
954 fn log2_of_two_is_one() {
955 let two = D38s12::from_bits(2_000_000_000_000);
956 let result = two.log2();
957 assert!(
958 within(result, 1_000_000_000_000, DERIVED_LOG_TOLERANCE_LSB),
959 "log2(2) bits = {}",
960 result.to_bits()
961 );
962 }
963
964 #[test]
966 fn log2_of_eight_is_three() {
967 let eight = D38s12::from_bits(8_000_000_000_000);
968 let result = eight.log2();
969 assert!(
970 within(result, 3_000_000_000_000, DERIVED_LOG_TOLERANCE_LSB),
971 "log2(8) bits = {}",
972 result.to_bits()
973 );
974 }
975
976 #[test]
978 fn log10_of_ten_is_one() {
979 let ten = D38s12::from_bits(10_000_000_000_000);
980 let result = ten.log10();
981 assert!(
982 within(result, 1_000_000_000_000, DERIVED_LOG_TOLERANCE_LSB),
983 "log10(10) bits = {}",
984 result.to_bits()
985 );
986 }
987
988 #[test]
990 fn log10_of_hundred_is_two() {
991 let hundred = D38s12::from_bits(100_000_000_000_000);
992 let result = hundred.log10();
993 assert!(
994 within(result, 2_000_000_000_000, DERIVED_LOG_TOLERANCE_LSB),
995 "log10(100) bits = {}",
996 result.to_bits()
997 );
998 }
999
1000 #[test]
1002 fn log_self_is_one() {
1003 let base = D38s12::from_bits(5_000_000_000_000); let result = base.log(base);
1005 assert!(
1006 within(result, 1_000_000_000_000, DERIVED_LOG_TOLERANCE_LSB),
1007 "log_5(5) bits = {}",
1008 result.to_bits()
1009 );
1010 }
1011
1012 #[test]
1014 fn log_with_base_two() {
1015 let eight = D38s12::from_bits(8_000_000_000_000);
1016 let two = D38s12::from_bits(2_000_000_000_000);
1017 let result = eight.log(two);
1018 assert!(
1019 within(result, 3_000_000_000_000, DERIVED_LOG_TOLERANCE_LSB),
1020 "log_2(8) bits = {}",
1021 result.to_bits()
1022 );
1023 }
1024
1025 #[test]
1026 #[should_panic(expected = "base must not equal 1")]
1027 fn log_base_one_panics() {
1028 let x = D38s12::from_bits(5_000_000_000_000);
1029 let one = D38s12::ONE;
1030 let _ = x.log(one);
1031 }
1032
1033 const EXP_TOLERANCE_LSB: i128 = 20;
1036
1037 #[test]
1039 fn exp_of_zero_is_one() {
1040 assert_eq!(D38s12::ZERO.exp(), D38s12::ONE);
1041 }
1042
1043 #[test]
1045 fn exp_of_one_is_e() {
1046 let result = D38s12::ONE.exp();
1047 assert!(
1049 within(result, 2_718_281_828_459, EXP_TOLERANCE_LSB),
1050 "exp(1) bits = {}",
1051 result.to_bits()
1052 );
1053 }
1054
1055 #[test]
1057 fn exp_of_ln_2_is_two() {
1058 let ln_2 = D38s12::from_bits(693_147_180_560);
1059 let result = ln_2.exp();
1060 assert!(
1061 within(result, 2_000_000_000_000, EXP_TOLERANCE_LSB),
1062 "exp(ln 2) bits = {}",
1063 result.to_bits()
1064 );
1065 }
1066
1067 #[test]
1069 fn exp_of_negative_one_is_reciprocal_e() {
1070 let neg_one = D38s12::from_bits(-1_000_000_000_000);
1071 let result = neg_one.exp();
1072 assert!(
1074 within(result, 367_879_441_171, EXP_TOLERANCE_LSB),
1075 "exp(-1) bits = {}",
1076 result.to_bits()
1077 );
1078 }
1079
1080 #[test]
1082 fn exp2_of_zero_is_one() {
1083 assert_eq!(D38s12::ZERO.exp2(), D38s12::ONE);
1084 }
1085
1086 #[test]
1088 fn exp2_of_one_is_two() {
1089 let result = D38s12::ONE.exp2();
1090 assert!(
1091 within(result, 2_000_000_000_000, EXP_TOLERANCE_LSB),
1092 "exp2(1) bits = {}",
1093 result.to_bits()
1094 );
1095 }
1096
1097 #[test]
1099 fn exp2_of_ten_is_1024() {
1100 let ten = D38s12::from_bits(10_000_000_000_000);
1101 let result = ten.exp2();
1102 assert!(
1103 within(result, 1_024_000_000_000_000, EXP_TOLERANCE_LSB * 10),
1104 "exp2(10) bits = {}",
1105 result.to_bits()
1106 );
1107 }
1108}
1109