1use crate::types::widths::D38;
73
74pub(crate) use crate::algos::ln::fixed_d38::STRICT_GUARD;
79
80impl<const SCALE: u32> D38<SCALE> {
81 #[inline]
108 #[must_use]
109 pub fn ln_strict(self) -> Self {
110 self.ln_strict_with(crate::support::rounding::DEFAULT_ROUNDING_MODE)
111 }
112
113 #[inline]
115 #[must_use]
116 pub fn ln_strict_with(self, mode: crate::support::rounding::RoundingMode) -> Self {
117 <Self as crate::policy::ln::LnPolicy>::ln_impl(self, mode)
118 }
119
120 #[inline]
124 #[must_use]
125 pub fn ln_approx(self, working_digits: u32) -> Self {
126 self.ln_approx_with(working_digits, crate::support::rounding::DEFAULT_ROUNDING_MODE)
127 }
128
129 #[inline]
131 #[must_use]
132 pub fn ln_approx_with(self, working_digits: u32, mode: crate::support::rounding::RoundingMode) -> Self {
133 if working_digits == STRICT_GUARD {
134 return self.ln_strict_with(mode);
135 }
136 <Self as crate::policy::ln::LnPolicy>::ln_with_impl(self, working_digits, mode)
137 }
138
139 #[cfg(all(feature = "strict", not(feature = "fast")))]
141 #[inline]
142 #[must_use]
143 pub fn ln(self) -> Self {
144 self.ln_strict()
145 }
146
147 #[inline]
149 #[must_use]
150 pub fn log_strict(self, base: Self) -> Self {
151 self.log_strict_with(base, crate::support::rounding::DEFAULT_ROUNDING_MODE)
152 }
153
154 #[inline]
156 #[must_use]
157 pub fn log_strict_with(self, base: Self, mode: crate::support::rounding::RoundingMode) -> Self {
158 <Self as crate::policy::ln::LnPolicy>::log_impl(self, base, mode)
159 }
160
161 #[inline]
163 #[must_use]
164 pub fn log_approx(self, base: Self, working_digits: u32) -> Self {
165 self.log_approx_with(base, working_digits, crate::support::rounding::DEFAULT_ROUNDING_MODE)
166 }
167
168 #[inline]
170 #[must_use]
171 pub fn log_approx_with(self, base: Self, working_digits: u32, mode: crate::support::rounding::RoundingMode) -> Self {
172 if working_digits == STRICT_GUARD {
173 return self.log_strict_with(base, mode);
174 }
175 <Self as crate::policy::ln::LnPolicy>::log_with_impl(self, base, working_digits, mode)
176 }
177
178 #[cfg(all(feature = "strict", not(feature = "fast")))]
180 #[inline]
181 #[must_use]
182 pub fn log(self, base: Self) -> Self {
183 self.log_strict(base)
184 }
185
186 #[inline]
188 #[must_use]
189 pub fn log2_strict(self) -> Self {
190 self.log2_strict_with(crate::support::rounding::DEFAULT_ROUNDING_MODE)
191 }
192
193 #[inline]
195 #[must_use]
196 pub fn log2_strict_with(self, mode: crate::support::rounding::RoundingMode) -> Self {
197 <Self as crate::policy::ln::LnPolicy>::log2_impl(self, mode)
198 }
199
200 #[inline]
202 #[must_use]
203 pub fn log2_approx(self, working_digits: u32) -> Self {
204 self.log2_approx_with(working_digits, crate::support::rounding::DEFAULT_ROUNDING_MODE)
205 }
206
207 #[inline]
209 #[must_use]
210 pub fn log2_approx_with(self, working_digits: u32, mode: crate::support::rounding::RoundingMode) -> Self {
211 if working_digits == STRICT_GUARD {
212 return self.log2_strict_with(mode);
213 }
214 <Self as crate::policy::ln::LnPolicy>::log2_with_impl(self, working_digits, mode)
215 }
216
217 #[cfg(all(feature = "strict", not(feature = "fast")))]
219 #[inline]
220 #[must_use]
221 pub fn log2(self) -> Self {
222 self.log2_strict()
223 }
224
225 #[inline]
227 #[must_use]
228 pub fn log10_strict(self) -> Self {
229 self.log10_strict_with(crate::support::rounding::DEFAULT_ROUNDING_MODE)
230 }
231
232 #[inline]
234 #[must_use]
235 pub fn log10_strict_with(self, mode: crate::support::rounding::RoundingMode) -> Self {
236 <Self as crate::policy::ln::LnPolicy>::log10_impl(self, mode)
237 }
238
239 #[inline]
241 #[must_use]
242 pub fn log10_approx(self, working_digits: u32) -> Self {
243 self.log10_approx_with(working_digits, crate::support::rounding::DEFAULT_ROUNDING_MODE)
244 }
245
246 #[inline]
248 #[must_use]
249 pub fn log10_approx_with(self, working_digits: u32, mode: crate::support::rounding::RoundingMode) -> Self {
250 if working_digits == STRICT_GUARD {
251 return self.log10_strict_with(mode);
252 }
253 <Self as crate::policy::ln::LnPolicy>::log10_with_impl(self, working_digits, mode)
254 }
255
256 #[cfg(all(feature = "strict", not(feature = "fast")))]
258 #[inline]
259 #[must_use]
260 pub fn log10(self) -> Self {
261 self.log10_strict()
262 }
263
264 #[inline]
268 #[must_use]
269 pub fn exp_strict(self) -> Self {
270 self.exp_strict_with(crate::support::rounding::DEFAULT_ROUNDING_MODE)
271 }
272
273 #[inline]
275 #[must_use]
276 pub fn exp_strict_with(self, mode: crate::support::rounding::RoundingMode) -> Self {
277 <Self as crate::policy::exp::ExpPolicy>::exp_impl(self, mode)
278 }
279
280 #[inline]
282 #[must_use]
283 pub fn exp_approx(self, working_digits: u32) -> Self {
284 self.exp_approx_with(working_digits, crate::support::rounding::DEFAULT_ROUNDING_MODE)
285 }
286
287 #[inline]
289 #[must_use]
290 pub fn exp_approx_with(self, working_digits: u32, mode: crate::support::rounding::RoundingMode) -> Self {
291 if working_digits == STRICT_GUARD {
292 return self.exp_strict_with(mode);
293 }
294 <Self as crate::policy::exp::ExpPolicy>::exp_with_impl(self, working_digits, mode)
295 }
296
297 #[cfg(all(feature = "strict", not(feature = "fast")))]
299 #[inline]
300 #[must_use]
301 pub fn exp(self) -> Self {
302 self.exp_strict()
303 }
304
305 #[inline]
307 #[must_use]
308 pub fn exp2_strict(self) -> Self {
309 self.exp2_strict_with(crate::support::rounding::DEFAULT_ROUNDING_MODE)
310 }
311
312 #[inline]
314 #[must_use]
315 pub fn exp2_strict_with(self, mode: crate::support::rounding::RoundingMode) -> Self {
316 <Self as crate::policy::exp::ExpPolicy>::exp2_impl(self, mode)
317 }
318
319 #[inline]
321 #[must_use]
322 pub fn exp2_approx(self, working_digits: u32) -> Self {
323 self.exp2_approx_with(working_digits, crate::support::rounding::DEFAULT_ROUNDING_MODE)
324 }
325
326 #[inline]
328 #[must_use]
329 pub fn exp2_approx_with(self, working_digits: u32, mode: crate::support::rounding::RoundingMode) -> Self {
330 if working_digits == STRICT_GUARD {
331 return self.exp2_strict_with(mode);
332 }
333 <Self as crate::policy::exp::ExpPolicy>::exp2_with_impl(self, working_digits, mode)
334 }
335
336 #[cfg(all(feature = "strict", not(feature = "fast")))]
338 #[inline]
339 #[must_use]
340 pub fn exp2(self) -> Self {
341 self.exp2_strict()
342 }
343}
344
345#[cfg(all(test, feature = "strict", not(feature = "fast")))]
346mod strict_tests {
347 use crate::types::widths::D38s12;
348
349 const STRICT_TOLERANCE_LSB: i128 = 2;
353
354 fn within(actual: D38s12, expected_bits: i128, tolerance: i128) -> bool {
355 (actual.to_bits() - expected_bits).abs() <= tolerance
356 }
357
358 #[test]
360 fn ln_of_one_is_zero() {
361 assert_eq!(D38s12::ONE.ln(), D38s12::ZERO);
362 }
363
364 #[test]
369 fn ln_strict_is_correctly_rounded_vs_f64() {
370 use crate::types::widths::D38;
371 fn check(raw: i128) {
372 let x = D38::<9>::from_bits(raw);
373 let strict = x.ln_strict().to_bits();
374 let reference = {
375 let v = raw as f64 / 1e9;
376 (v.ln() * 1e9).round() as i128
377 };
378 assert!(
379 (strict - reference).abs() <= 1,
380 "ln_strict({raw}) = {strict}, f64 reference {reference}"
381 );
382 }
383 for &raw in &[
384 1,
385 500_000_000,
386 1_000_000_000,
387 1_500_000_000,
388 2_000_000_000,
389 2_718_281_828,
390 10_000_000_000,
391 123_456_789_012_345,
392 999_999_999_999_999_999,
393 i64::MAX as i128,
394 ] {
395 check(raw);
396 }
397 }
398
399 #[test]
402 fn strict_log_exp_family_matches_f64() {
403 use crate::types::widths::D38;
404 fn check_exp(raw: i128) {
405 let x = D38::<9>::from_bits(raw);
406 let strict = x.exp_strict().to_bits();
407 let reference = ((raw as f64 / 1e9).exp() * 1e9).round() as i128;
408 assert!(
409 (strict - reference).abs() <= 1,
410 "exp_strict({raw}) = {strict}, f64 reference {reference}"
411 );
412 }
413 fn check_log2(raw: i128) {
414 let x = D38::<9>::from_bits(raw);
415 let strict = x.log2_strict().to_bits();
416 let reference = ((raw as f64 / 1e9).log2() * 1e9).round() as i128;
417 assert!(
418 (strict - reference).abs() <= 1,
419 "log2_strict({raw}) = {strict}, f64 reference {reference}"
420 );
421 }
422 fn check_log10(raw: i128) {
423 let x = D38::<9>::from_bits(raw);
424 let strict = x.log10_strict().to_bits();
425 let reference = ((raw as f64 / 1e9).log10() * 1e9).round() as i128;
426 assert!(
427 (strict - reference).abs() <= 1,
428 "log10_strict({raw}) = {strict}, f64 reference {reference}"
429 );
430 }
431 for &raw in &[
432 -5_000_000_000, -1_000_000_000, -500_000_000, 1, 500_000_000,
433 1_000_000_000, 2_000_000_000, 5_000_000_000, 10_000_000_000,
434 ] {
435 check_exp(raw);
436 }
437 for &raw in &[
438 1, 500_000_000, 1_000_000_000, 2_000_000_000, 8_000_000_000,
439 10_000_000_000, 123_456_789_012_345, i64::MAX as i128,
440 ] {
441 check_log2(raw);
442 check_log10(raw);
443 }
444 }
445
446 #[test]
448 fn strict_exp2_at_integers() {
449 use crate::types::widths::D38;
450 for k in 0_i128..=12 {
451 let x = D38::<12>::from_bits(k * 10i128.pow(12));
452 let got = x.exp2_strict().to_bits();
453 let expected = (1i128 << k) * 10i128.pow(12);
454 assert_eq!(got, expected, "2^{k}");
455 }
456 }
457
458 #[test]
460 fn ln_strict_of_powers_of_two() {
461 use crate::types::widths::D38;
462 let ln2_s18: i128 = 693_147_180_559_945_309;
463 for k in 1_i128..=20 {
464 let x = D38::<18>::from_bits((1i128 << k) * 10i128.pow(18));
465 let got = x.ln_strict().to_bits();
466 let expected = k * ln2_s18;
467 let tol = k / 2 + 2;
468 assert!(
469 (got - expected).abs() <= tol,
470 "ln(2^{k}) = {got}, expected ≈ {expected}"
471 );
472 }
473 }
474
475 #[test]
477 fn ln_of_two_close_to_canonical() {
478 let two = D38s12::from_bits(2_000_000_000_000);
479 let result = two.ln();
480 assert!(
481 within(result, 693_147_180_560, STRICT_TOLERANCE_LSB),
482 "ln(2) bits = {}",
483 result.to_bits()
484 );
485 }
486
487 #[test]
489 fn ln_of_e_close_to_one() {
490 let e_at_s12 = D38s12::from_bits(2_718_281_828_459);
491 let result = e_at_s12.ln();
492 assert!(
493 within(result, 1_000_000_000_000, STRICT_TOLERANCE_LSB),
494 "ln(e) bits = {}, expected ~1_000_000_000_000",
495 result.to_bits()
496 );
497 }
498
499 #[test]
501 fn ln_of_ten_close_to_canonical() {
502 let ten = D38s12::from_bits(10_000_000_000_000);
503 let result = ten.ln();
504 assert!(
505 within(result, 2_302_585_092_994, STRICT_TOLERANCE_LSB),
506 "ln(10) bits = {}, expected ~2_302_585_092_994",
507 result.to_bits()
508 );
509 }
510
511 #[test]
513 fn ln_above_one_is_positive() {
514 let v = D38s12::from_bits(1_500_000_000_000);
515 let result = v.ln();
516 assert!(result.to_bits() > 0);
517 }
518
519 #[test]
521 fn ln_below_one_is_negative() {
522 let v = D38s12::from_bits(500_000_000_000);
523 let result = v.ln();
524 assert!(result.to_bits() < 0);
525 assert!(
526 within(result, -693_147_180_560, STRICT_TOLERANCE_LSB),
527 "ln(0.5) bits = {}, expected ~-693_147_180_560",
528 result.to_bits()
529 );
530 }
531
532 #[test]
533 #[should_panic(expected = "argument must be positive")]
534 fn ln_of_zero_panics() {
535 let _ = D38s12::ZERO.ln();
536 }
537
538 #[test]
539 #[should_panic(expected = "argument must be positive")]
540 fn ln_of_negative_panics() {
541 let neg = D38s12::from_bits(-1_000_000_000_000);
542 let _ = neg.ln();
543 }
544
545 const DERIVED_LOG_TOLERANCE_LSB: i128 = 20;
548
549 #[test]
551 fn log2_of_two_is_one() {
552 let two = D38s12::from_bits(2_000_000_000_000);
553 let result = two.log2();
554 assert!(
555 within(result, 1_000_000_000_000, DERIVED_LOG_TOLERANCE_LSB),
556 "log2(2) bits = {}",
557 result.to_bits()
558 );
559 }
560
561 #[test]
563 fn log2_of_eight_is_three() {
564 let eight = D38s12::from_bits(8_000_000_000_000);
565 let result = eight.log2();
566 assert!(
567 within(result, 3_000_000_000_000, DERIVED_LOG_TOLERANCE_LSB),
568 "log2(8) bits = {}",
569 result.to_bits()
570 );
571 }
572
573 #[test]
575 fn log10_of_ten_is_one() {
576 let ten = D38s12::from_bits(10_000_000_000_000);
577 let result = ten.log10();
578 assert!(
579 within(result, 1_000_000_000_000, DERIVED_LOG_TOLERANCE_LSB),
580 "log10(10) bits = {}",
581 result.to_bits()
582 );
583 }
584
585 #[test]
587 fn log10_of_hundred_is_two() {
588 let hundred = D38s12::from_bits(100_000_000_000_000);
589 let result = hundred.log10();
590 assert!(
591 within(result, 2_000_000_000_000, DERIVED_LOG_TOLERANCE_LSB),
592 "log10(100) bits = {}",
593 result.to_bits()
594 );
595 }
596
597 #[test]
599 fn log_self_is_one() {
600 let base = D38s12::from_bits(5_000_000_000_000);
601 let result = base.log(base);
602 assert!(
603 within(result, 1_000_000_000_000, DERIVED_LOG_TOLERANCE_LSB),
604 "log_5(5) bits = {}",
605 result.to_bits()
606 );
607 }
608
609 #[test]
611 fn log_with_base_two() {
612 let eight = D38s12::from_bits(8_000_000_000_000);
613 let two = D38s12::from_bits(2_000_000_000_000);
614 let result = eight.log(two);
615 assert!(
616 within(result, 3_000_000_000_000, DERIVED_LOG_TOLERANCE_LSB),
617 "log_2(8) bits = {}",
618 result.to_bits()
619 );
620 }
621
622 #[test]
623 #[should_panic(expected = "base must not equal 1")]
624 fn log_base_one_panics() {
625 let x = D38s12::from_bits(5_000_000_000_000);
626 let one = D38s12::ONE;
627 let _ = x.log(one);
628 }
629
630 const EXP_TOLERANCE_LSB: i128 = 20;
633
634 #[test]
636 fn exp_of_zero_is_one() {
637 assert_eq!(D38s12::ZERO.exp(), D38s12::ONE);
638 }
639
640 #[test]
642 fn exp_of_one_is_e() {
643 let result = D38s12::ONE.exp();
644 assert!(
645 within(result, 2_718_281_828_459, EXP_TOLERANCE_LSB),
646 "exp(1) bits = {}",
647 result.to_bits()
648 );
649 }
650
651 #[test]
653 fn exp_of_ln_2_is_two() {
654 let ln_2 = D38s12::from_bits(693_147_180_560);
655 let result = ln_2.exp();
656 assert!(
657 within(result, 2_000_000_000_000, EXP_TOLERANCE_LSB),
658 "exp(ln 2) bits = {}",
659 result.to_bits()
660 );
661 }
662
663 #[test]
665 fn exp_of_negative_one_is_reciprocal_e() {
666 let neg_one = D38s12::from_bits(-1_000_000_000_000);
667 let result = neg_one.exp();
668 assert!(
669 within(result, 367_879_441_171, EXP_TOLERANCE_LSB),
670 "exp(-1) bits = {}",
671 result.to_bits()
672 );
673 }
674
675 #[test]
677 fn exp2_of_zero_is_one() {
678 assert_eq!(D38s12::ZERO.exp2(), D38s12::ONE);
679 }
680
681 #[test]
683 fn exp2_of_one_is_two() {
684 let result = D38s12::ONE.exp2();
685 assert!(
686 within(result, 2_000_000_000_000, EXP_TOLERANCE_LSB),
687 "exp2(1) bits = {}",
688 result.to_bits()
689 );
690 }
691
692 #[test]
694 fn exp2_of_ten_is_1024() {
695 let ten = D38s12::from_bits(10_000_000_000_000);
696 let result = ten.exp2();
697 assert!(
698 within(result, 1_024_000_000_000_000, EXP_TOLERANCE_LSB * 10),
699 "exp2(10) bits = {}",
700 result.to_bits()
701 );
702 }
703}