1pub(crate) use crate::algos::ln::ln_series_2limb::STRICT_GUARD;
81
82impl<const SCALE: u32> crate::D<crate::int::types::Int<2>, SCALE> {
83 #[inline]
110 #[must_use]
111 pub fn ln_strict(self) -> Self {
112 self.ln_strict_with(crate::support::rounding::DEFAULT_ROUNDING_MODE)
113 }
114
115 #[inline]
117 #[must_use]
118 pub fn ln_strict_with(self, mode: crate::support::rounding::RoundingMode) -> Self {
119 Self::from_bits(crate::policy::ln::dispatch::<_, SCALE>(self.to_bits(), mode))
120 }
121
122 #[inline]
126 #[must_use]
127 pub fn ln_approx(self, working_digits: u32) -> Self {
128 self.ln_approx_with(
129 working_digits,
130 crate::support::rounding::DEFAULT_ROUNDING_MODE,
131 )
132 }
133
134 #[inline]
136 #[must_use]
137 pub fn ln_approx_with(
138 self,
139 working_digits: u32,
140 mode: crate::support::rounding::RoundingMode,
141 ) -> Self {
142 if working_digits == STRICT_GUARD {
143 return self.ln_strict_with(mode);
144 }
145 Self::from_bits(crate::policy::ln::dispatch_with::<_, SCALE>(self.to_bits(), working_digits, mode))
146 }
147
148 #[cfg(all(feature = "strict", not(feature = "fast")))]
150 #[inline]
151 #[must_use]
152 pub fn ln(self) -> Self {
153 self.ln_strict()
154 }
155
156 #[inline]
158 #[must_use]
159 pub fn log_strict(self, base: Self) -> Self {
160 self.log_strict_with(base, crate::support::rounding::DEFAULT_ROUNDING_MODE)
161 }
162
163 #[inline]
165 #[must_use]
166 pub fn log_strict_with(self, base: Self, mode: crate::support::rounding::RoundingMode) -> Self {
167 Self::from_bits(crate::policy::log::dispatch::<_, SCALE>(self.to_bits(), base.to_bits(), mode))
168 }
169
170 #[inline]
172 #[must_use]
173 pub fn log_approx(self, base: Self, working_digits: u32) -> Self {
174 self.log_approx_with(
175 base,
176 working_digits,
177 crate::support::rounding::DEFAULT_ROUNDING_MODE,
178 )
179 }
180
181 #[inline]
183 #[must_use]
184 pub fn log_approx_with(
185 self,
186 base: Self,
187 working_digits: u32,
188 mode: crate::support::rounding::RoundingMode,
189 ) -> Self {
190 if working_digits == STRICT_GUARD {
191 return self.log_strict_with(base, mode);
192 }
193 Self::from_bits(crate::policy::log::dispatch_with::<_, SCALE>(self.to_bits(), base.to_bits(), working_digits, mode))
194 }
195
196 #[cfg(all(feature = "strict", not(feature = "fast")))]
198 #[inline]
199 #[must_use]
200 pub fn log(self, base: Self) -> Self {
201 self.log_strict(base)
202 }
203
204 #[inline]
206 #[must_use]
207 pub fn log2_strict(self) -> Self {
208 self.log2_strict_with(crate::support::rounding::DEFAULT_ROUNDING_MODE)
209 }
210
211 #[inline]
213 #[must_use]
214 pub fn log2_strict_with(self, mode: crate::support::rounding::RoundingMode) -> Self {
215 Self::from_bits(crate::policy::ln::log2_dispatch::<_, SCALE>(self.to_bits(), mode))
216 }
217
218 #[inline]
220 #[must_use]
221 pub fn log2_approx(self, working_digits: u32) -> Self {
222 self.log2_approx_with(
223 working_digits,
224 crate::support::rounding::DEFAULT_ROUNDING_MODE,
225 )
226 }
227
228 #[inline]
230 #[must_use]
231 pub fn log2_approx_with(
232 self,
233 working_digits: u32,
234 mode: crate::support::rounding::RoundingMode,
235 ) -> Self {
236 if working_digits == STRICT_GUARD {
237 return self.log2_strict_with(mode);
238 }
239 Self::from_bits(crate::policy::ln::log2_dispatch_with::<_, SCALE>(self.to_bits(), working_digits, mode))
240 }
241
242 #[cfg(all(feature = "strict", not(feature = "fast")))]
244 #[inline]
245 #[must_use]
246 pub fn log2(self) -> Self {
247 self.log2_strict()
248 }
249
250 #[inline]
252 #[must_use]
253 pub fn log10_strict(self) -> Self {
254 self.log10_strict_with(crate::support::rounding::DEFAULT_ROUNDING_MODE)
255 }
256
257 #[inline]
259 #[must_use]
260 pub fn log10_strict_with(self, mode: crate::support::rounding::RoundingMode) -> Self {
261 Self::from_bits(crate::policy::ln::log10_dispatch::<_, SCALE>(self.to_bits(), mode))
262 }
263
264 #[inline]
266 #[must_use]
267 pub fn log10_approx(self, working_digits: u32) -> Self {
268 self.log10_approx_with(
269 working_digits,
270 crate::support::rounding::DEFAULT_ROUNDING_MODE,
271 )
272 }
273
274 #[inline]
276 #[must_use]
277 pub fn log10_approx_with(
278 self,
279 working_digits: u32,
280 mode: crate::support::rounding::RoundingMode,
281 ) -> Self {
282 if working_digits == STRICT_GUARD {
283 return self.log10_strict_with(mode);
284 }
285 Self::from_bits(crate::policy::ln::log10_dispatch_with::<_, SCALE>(self.to_bits(), working_digits, mode))
286 }
287
288 #[cfg(all(feature = "strict", not(feature = "fast")))]
290 #[inline]
291 #[must_use]
292 pub fn log10(self) -> Self {
293 self.log10_strict()
294 }
295
296 #[inline]
300 #[must_use]
301 pub fn exp_strict(self) -> Self {
302 self.exp_strict_with(crate::support::rounding::DEFAULT_ROUNDING_MODE)
303 }
304
305 #[inline]
307 #[must_use]
308 pub fn exp_strict_with(self, mode: crate::support::rounding::RoundingMode) -> Self {
309 Self::from_bits(crate::policy::exp::dispatch::<_, SCALE>(self.to_bits(), mode))
310 }
311
312 #[inline]
314 #[must_use]
315 pub fn exp_approx(self, working_digits: u32) -> Self {
316 self.exp_approx_with(
317 working_digits,
318 crate::support::rounding::DEFAULT_ROUNDING_MODE,
319 )
320 }
321
322 #[inline]
324 #[must_use]
325 pub fn exp_approx_with(
326 self,
327 working_digits: u32,
328 mode: crate::support::rounding::RoundingMode,
329 ) -> Self {
330 if working_digits == STRICT_GUARD {
331 return self.exp_strict_with(mode);
332 }
333 Self::from_bits(crate::policy::exp::dispatch_with::<_, SCALE>(self.to_bits(), working_digits, mode))
334 }
335
336 #[cfg(all(feature = "strict", not(feature = "fast")))]
338 #[inline]
339 #[must_use]
340 pub fn exp(self) -> Self {
341 self.exp_strict()
342 }
343
344 #[inline]
346 #[must_use]
347 pub fn exp2_strict(self) -> Self {
348 self.exp2_strict_with(crate::support::rounding::DEFAULT_ROUNDING_MODE)
349 }
350
351 #[inline]
353 #[must_use]
354 pub fn exp2_strict_with(self, mode: crate::support::rounding::RoundingMode) -> Self {
355 Self::from_bits(crate::policy::exp::exp2_dispatch::<_, SCALE>(self.to_bits(), mode))
356 }
357
358 #[inline]
360 #[must_use]
361 pub fn exp2_approx(self, working_digits: u32) -> Self {
362 self.exp2_approx_with(
363 working_digits,
364 crate::support::rounding::DEFAULT_ROUNDING_MODE,
365 )
366 }
367
368 #[inline]
370 #[must_use]
371 pub fn exp2_approx_with(
372 self,
373 working_digits: u32,
374 mode: crate::support::rounding::RoundingMode,
375 ) -> Self {
376 if working_digits == STRICT_GUARD {
377 return self.exp2_strict_with(mode);
378 }
379 Self::from_bits(crate::policy::exp::exp2_dispatch_with::<_, SCALE>(self.to_bits(), working_digits, mode))
380 }
381
382 #[cfg(all(feature = "strict", not(feature = "fast")))]
384 #[inline]
385 #[must_use]
386 pub fn exp2(self) -> Self {
387 self.exp2_strict()
388 }
389}
390
391#[cfg(all(test, feature = "strict", not(feature = "fast")))]
392mod strict_tests {
393 use crate::types::widths::D38s12;
394
395 const STRICT_TOLERANCE_LSB: i128 = 2;
399
400 fn within(actual: D38s12, expected_bits: i128, tolerance: i128) -> bool {
401 (actual.to_bits().as_i128() - expected_bits).abs() <= tolerance
402 }
403
404 #[test]
406 fn ln_of_one_is_zero() {
407 assert_eq!(D38s12::ONE.ln(), D38s12::ZERO);
408 }
409
410 #[test]
415 fn ln_strict_is_correctly_rounded_vs_f64() {
416 fn check(raw: i128) {
417 let x = crate::D::<crate::int::types::Int<2>, 9>::from_bits(crate::int::types::Int::<2>::from_i128(raw));
418 let strict = x.ln_strict().to_bits().as_i128();
419 let reference = {
420 let v = raw as f64 / 1e9;
421 (v.ln() * 1e9).round() as i128
422 };
423 assert!(
424 (strict - reference).abs() <= 1,
425 "ln_strict({raw}) = {strict}, f64 reference {reference}"
426 );
427 }
428 for &raw in &[
429 1,
430 500_000_000,
431 1_000_000_000,
432 1_500_000_000,
433 2_000_000_000,
434 2_718_281_828,
435 10_000_000_000,
436 123_456_789_012_345,
437 999_999_999_999_999_999,
438 i64::MAX as i128,
439 ] {
440 check(raw);
441 }
442 }
443
444 #[test]
447 fn strict_log_exp_family_matches_f64() {
448 fn check_exp(raw: i128) {
449 let x = crate::D::<crate::int::types::Int<2>, 9>::from_bits(crate::int::types::Int::<2>::from_i128(raw));
450 let strict = x.exp_strict().to_bits().as_i128();
451 let reference = ((raw as f64 / 1e9).exp() * 1e9).round() as i128;
452 assert!(
453 (strict - reference).abs() <= 1,
454 "exp_strict({raw}) = {strict}, f64 reference {reference}"
455 );
456 }
457 fn check_log2(raw: i128) {
458 let x = crate::D::<crate::int::types::Int<2>, 9>::from_bits(crate::int::types::Int::<2>::from_i128(raw));
459 let strict = x.log2_strict().to_bits().as_i128();
460 let reference = ((raw as f64 / 1e9).log2() * 1e9).round() as i128;
461 assert!(
462 (strict - reference).abs() <= 1,
463 "log2_strict({raw}) = {strict}, f64 reference {reference}"
464 );
465 }
466 fn check_log10(raw: i128) {
467 let x = crate::D::<crate::int::types::Int<2>, 9>::from_bits(crate::int::types::Int::<2>::from_i128(raw));
468 let strict = x.log10_strict().to_bits().as_i128();
469 let reference = ((raw as f64 / 1e9).log10() * 1e9).round() as i128;
470 assert!(
471 (strict - reference).abs() <= 1,
472 "log10_strict({raw}) = {strict}, f64 reference {reference}"
473 );
474 }
475 for &raw in &[
476 -5_000_000_000,
477 -1_000_000_000,
478 -500_000_000,
479 1,
480 500_000_000,
481 1_000_000_000,
482 2_000_000_000,
483 5_000_000_000,
484 10_000_000_000,
485 ] {
486 check_exp(raw);
487 }
488 for &raw in &[
489 1,
490 500_000_000,
491 1_000_000_000,
492 2_000_000_000,
493 8_000_000_000,
494 10_000_000_000,
495 123_456_789_012_345,
496 i64::MAX as i128,
497 ] {
498 check_log2(raw);
499 check_log10(raw);
500 }
501 }
502
503 #[test]
505 fn strict_exp2_at_integers() {
506 for k in 0_i128..=12 {
507 let x = crate::D::<crate::int::types::Int<2>, 12>::from_bits(crate::int::types::Int::<2>::from_i128(k * 10i128.pow(12)));
508 let got = x.exp2_strict().to_bits().as_i128();
509 let expected = (1i128 << k) * 10i128.pow(12);
510 assert_eq!(got, expected, "2^{k}");
511 }
512 }
513
514 #[test]
516 fn ln_strict_of_powers_of_two() {
517 let ln2_s18: i128 = 693_147_180_559_945_309;
518 for k in 1_i128..=20 {
519 let x = crate::D::<crate::int::types::Int<2>, 18>::from_bits(crate::int::types::Int::<2>::from_i128((1i128 << k) * 10i128.pow(18)));
520 let got = x.ln_strict().to_bits().as_i128();
521 let expected = k * ln2_s18;
522 let tol = k / 2 + 2;
523 assert!(
524 (got - expected).abs() <= tol,
525 "ln(2^{k}) = {got}, expected ≈ {expected}"
526 );
527 }
528 }
529
530 #[test]
532 fn ln_of_two_close_to_canonical() {
533 let two = D38s12::from_bits(crate::int::types::Int::<2>::from_i128(2_000_000_000_000));
534 let result = two.ln();
535 assert!(
536 within(result, 693_147_180_560, STRICT_TOLERANCE_LSB),
537 "ln(2) bits = {}",
538 result.to_bits().as_i128()
539 );
540 }
541
542 #[test]
544 fn ln_of_e_close_to_one() {
545 let e_at_s12 = D38s12::from_bits(crate::int::types::Int::<2>::from_i128(2_718_281_828_459));
546 let result = e_at_s12.ln();
547 assert!(
548 within(result, 1_000_000_000_000, STRICT_TOLERANCE_LSB),
549 "ln(e) bits = {}, expected ~1_000_000_000_000",
550 result.to_bits().as_i128()
551 );
552 }
553
554 #[test]
556 fn ln_of_ten_close_to_canonical() {
557 let ten = D38s12::from_bits(crate::int::types::Int::<2>::from_i128(10_000_000_000_000));
558 let result = ten.ln();
559 assert!(
560 within(result, 2_302_585_092_994, STRICT_TOLERANCE_LSB),
561 "ln(10) bits = {}, expected ~2_302_585_092_994",
562 result.to_bits().as_i128()
563 );
564 }
565
566 #[test]
568 fn ln_above_one_is_positive() {
569 let v = D38s12::from_bits(crate::int::types::Int::<2>::from_i128(1_500_000_000_000));
570 let result = v.ln();
571 assert!(result.to_bits().as_i128() > 0);
572 }
573
574 #[test]
576 fn ln_below_one_is_negative() {
577 let v = D38s12::from_bits(crate::int::types::Int::<2>::from_i128(500_000_000_000));
578 let result = v.ln();
579 assert!(result.to_bits().as_i128() < 0);
580 assert!(
581 within(result, -693_147_180_560, STRICT_TOLERANCE_LSB),
582 "ln(0.5) bits = {}, expected ~-693_147_180_560",
583 result.to_bits().as_i128()
584 );
585 }
586
587 #[test]
588 #[should_panic(expected = "argument must be positive")]
589 fn ln_of_zero_panics() {
590 let _ = D38s12::ZERO.ln();
591 }
592
593 #[test]
594 #[should_panic(expected = "argument must be positive")]
595 fn ln_of_negative_panics() {
596 let neg = D38s12::from_bits(crate::int::types::Int::<2>::from_i128(-1_000_000_000_000));
597 let _ = neg.ln();
598 }
599
600 const DERIVED_LOG_TOLERANCE_LSB: i128 = 20;
603
604 #[test]
606 fn log2_of_two_is_one() {
607 let two = D38s12::from_bits(crate::int::types::Int::<2>::from_i128(2_000_000_000_000));
608 let result = two.log2();
609 assert!(
610 within(result, 1_000_000_000_000, DERIVED_LOG_TOLERANCE_LSB),
611 "log2(2) bits = {}",
612 result.to_bits().as_i128()
613 );
614 }
615
616 #[test]
618 fn log2_of_eight_is_three() {
619 let eight = D38s12::from_bits(crate::int::types::Int::<2>::from_i128(8_000_000_000_000));
620 let result = eight.log2();
621 assert!(
622 within(result, 3_000_000_000_000, DERIVED_LOG_TOLERANCE_LSB),
623 "log2(8) bits = {}",
624 result.to_bits().as_i128()
625 );
626 }
627
628 #[test]
630 fn log10_of_ten_is_one() {
631 let ten = D38s12::from_bits(crate::int::types::Int::<2>::from_i128(10_000_000_000_000));
632 let result = ten.log10();
633 assert!(
634 within(result, 1_000_000_000_000, DERIVED_LOG_TOLERANCE_LSB),
635 "log10(10) bits = {}",
636 result.to_bits().as_i128()
637 );
638 }
639
640 #[test]
642 fn log10_of_hundred_is_two() {
643 let hundred = D38s12::from_bits(crate::int::types::Int::<2>::from_i128(100_000_000_000_000));
644 let result = hundred.log10();
645 assert!(
646 within(result, 2_000_000_000_000, DERIVED_LOG_TOLERANCE_LSB),
647 "log10(100) bits = {}",
648 result.to_bits().as_i128()
649 );
650 }
651
652 #[test]
654 fn log_self_is_one() {
655 let base = D38s12::from_bits(crate::int::types::Int::<2>::from_i128(5_000_000_000_000));
656 let result = base.log(base);
657 assert!(
658 within(result, 1_000_000_000_000, DERIVED_LOG_TOLERANCE_LSB),
659 "log_5(5) bits = {}",
660 result.to_bits().as_i128()
661 );
662 }
663
664 #[test]
666 fn log_with_base_two() {
667 let eight = D38s12::from_bits(crate::int::types::Int::<2>::from_i128(8_000_000_000_000));
668 let two = D38s12::from_bits(crate::int::types::Int::<2>::from_i128(2_000_000_000_000));
669 let result = eight.log(two);
670 assert!(
671 within(result, 3_000_000_000_000, DERIVED_LOG_TOLERANCE_LSB),
672 "log_2(8) bits = {}",
673 result.to_bits().as_i128()
674 );
675 }
676
677 #[test]
678 #[should_panic(expected = "base must not equal 1")]
679 fn log_base_one_panics() {
680 let x = D38s12::from_bits(crate::int::types::Int::<2>::from_i128(5_000_000_000_000));
681 let one = D38s12::ONE;
682 let _ = x.log(one);
683 }
684
685 const EXP_TOLERANCE_LSB: i128 = 20;
688
689 #[test]
691 fn exp_of_zero_is_one() {
692 assert_eq!(D38s12::ZERO.exp(), D38s12::ONE);
693 }
694
695 #[test]
697 fn exp_of_one_is_e() {
698 let result = D38s12::ONE.exp();
699 assert!(
700 within(result, 2_718_281_828_459, EXP_TOLERANCE_LSB),
701 "exp(1) bits = {}",
702 result.to_bits().as_i128()
703 );
704 }
705
706 #[test]
708 fn exp_of_ln_2_is_two() {
709 let ln_2 = D38s12::from_bits(crate::int::types::Int::<2>::from_i128(693_147_180_560));
710 let result = ln_2.exp();
711 assert!(
712 within(result, 2_000_000_000_000, EXP_TOLERANCE_LSB),
713 "exp(ln 2) bits = {}",
714 result.to_bits().as_i128()
715 );
716 }
717
718 #[test]
720 fn exp_of_negative_one_is_reciprocal_e() {
721 let neg_one = D38s12::from_bits(crate::int::types::Int::<2>::from_i128(-1_000_000_000_000));
722 let result = neg_one.exp();
723 assert!(
724 within(result, 367_879_441_171, EXP_TOLERANCE_LSB),
725 "exp(-1) bits = {}",
726 result.to_bits().as_i128()
727 );
728 }
729
730 #[test]
732 fn exp2_of_zero_is_one() {
733 assert_eq!(D38s12::ZERO.exp2(), D38s12::ONE);
734 }
735
736 #[test]
738 fn exp2_of_one_is_two() {
739 let result = D38s12::ONE.exp2();
740 assert!(
741 within(result, 2_000_000_000_000, EXP_TOLERANCE_LSB),
742 "exp2(1) bits = {}",
743 result.to_bits().as_i128()
744 );
745 }
746
747 #[test]
749 fn exp2_of_ten_is_1024() {
750 let ten = D38s12::from_bits(crate::int::types::Int::<2>::from_i128(10_000_000_000_000));
751 let result = ten.exp2();
752 assert!(
753 within(result, 1_024_000_000_000_000, EXP_TOLERANCE_LSB * 10),
754 "exp2(10) bits = {}",
755 result.to_bits().as_i128()
756 );
757 }
758}