1use dashu_base::{
2 utils::{next_down, next_up},
3 AbsOrd,
4 Approximation::*,
5 EstimatedLog2, Sign,
6};
7use dashu_int::IBig;
8
9use crate::{
10 error::{assert_finite, assert_limited_precision, FpError, FpResult},
11 fbig::FBig,
12 math::cache::{reborrow_cache, ConstCache},
13 repr::{Context, Repr, Word},
14 round::{Round, Rounded},
15 utils::ceil_usize,
16};
17use core::cmp::Ordering;
18
19impl<const B: Word> EstimatedLog2 for Repr<B> {
20 fn log2_bounds(&self) -> (f32, f32) {
22 if self.significand.is_zero() {
23 return (f32::NEG_INFINITY, f32::NEG_INFINITY);
24 }
25
26 let (logs_lb, logs_ub) = self.significand.log2_bounds();
28 let (logb_lb, logb_ub) = if B.is_power_of_two() {
29 let log = B.trailing_zeros() as f32;
30 (log, log)
31 } else {
32 B.log2_bounds()
33 };
34 let e = self.exponent as f32;
35 let (lb, ub) = if self.exponent >= 0 {
36 (logs_lb + e * logb_lb, logs_ub + e * logb_ub)
37 } else {
38 (logs_lb + e * logb_ub, logs_ub + e * logb_lb)
39 };
40 (next_down(lb), next_up(ub))
41 }
42
43 fn log2_est(&self) -> f32 {
44 let logs = self.significand.log2_est();
45 let logb = if B.is_power_of_two() {
46 B.trailing_zeros() as f32
47 } else {
48 B.log2_est()
49 };
50 logs + self.exponent as f32 * logb
51 }
52}
53
54impl<R: Round, const B: Word> EstimatedLog2 for FBig<R, B> {
55 #[inline]
56 fn log2_bounds(&self) -> (f32, f32) {
57 self.repr.log2_bounds()
58 }
59
60 #[inline]
61 fn log2_est(&self) -> f32 {
62 self.repr.log2_est()
63 }
64}
65
66impl<R: Round, const B: Word> FBig<R, B> {
67 #[inline]
80 pub fn ln(&self) -> Self {
81 self.context.unwrap_fp(self.context.ln(&self.repr, None))
82 }
83
84 #[inline]
97 pub fn ln_1p(&self) -> Self {
98 self.context.unwrap_fp(self.context.ln_1p(&self.repr, None))
99 }
100
101 #[inline]
119 pub fn log2(&self) -> Self {
120 self.context.unwrap_fp(self.context.log2(&self.repr, None))
121 }
122}
123
124impl<R: Round> Context<R> {
125 #[inline]
129 fn ln2<const B: Word>(&self, cache: Option<&mut ConstCache>) -> FBig<R, B> {
130 if let Some(c) = cache {
131 return c.ln2::<B, R>(self.precision);
132 }
133 4 * self.iacoth(6.into()) + 2 * self.iacoth(99.into())
137 }
138
139 #[inline]
143 fn ln10<const B: Word>(&self, cache: Option<&mut ConstCache>) -> FBig<R, B> {
144 if let Some(c) = cache {
145 return c.ln10::<B, R>(self.precision);
146 }
147 3 * self.ln2(None) + 2 * self.iacoth(9.into())
149 }
150
151 #[inline]
155 pub(crate) fn ln_base<const B: Word>(&self, cache: Option<&mut ConstCache>) -> FBig<R, B> {
156 if let Some(c) = cache {
157 return c.ln_base::<B, R>(self.precision);
158 }
159 match B {
160 2 => self.ln2(None),
161 10 => self.ln10(None),
162 i if i.is_power_of_two() => self.ln2(None) * i.trailing_zeros(),
163 _ => self.unwrap_fp(self.ln(&Repr::new(Repr::<B>::BASE.into(), 0), None)),
164 }
165 }
166
167 fn iacoth<const B: Word>(&self, n: IBig) -> FBig<R, B> {
184 let n: u32 = (&n).try_into().expect("iacoth argument must fit in u32");
185
186 let log_b_n = n.log2_est() / B.log2_est();
190 let num_terms = (self.precision as f32 / (2.0 * log_b_n)) as usize + 10;
191
192 let (_p, q, t) = crate::math::cache::iacoth_bs(n, 1, num_terms + 1);
193
194 let guard_digits = ceil_usize(self.precision.log2_est() / B.log2_est());
197 let work_context = Self::new(self.precision + guard_digits + 2);
198
199 let num = work_context.convert_int::<B>(q.as_ibig() + &t).value();
200 let denom = work_context.convert_int::<B>(IBig::from(n) * &q).value();
201 num / denom
202 }
203
204 #[inline]
221 pub fn ln<const B: Word>(
222 &self,
223 x: &Repr<B>,
224 cache: Option<&mut ConstCache>,
225 ) -> FpResult<FBig<R, B>> {
226 if x.is_infinite() {
227 return Err(FpError::InfiniteInput);
228 }
229 if x.significand.is_zero() {
230 return Ok(Exact(FBig::new(Repr::neg_infinity(), *self)));
232 }
233 if x.sign() == Sign::Negative {
234 return Err(FpError::OutOfDomain);
235 }
236 Ok(self.ln_internal(x, false, cache))
237 }
238
239 #[inline]
263 pub fn log2<const B: Word>(
264 &self,
265 x: &Repr<B>,
266 cache: Option<&mut ConstCache>,
267 ) -> FpResult<FBig<R, B>> {
268 if x.is_infinite() {
269 return Err(FpError::InfiniteInput);
270 }
271 if x.significand.is_zero() {
272 return Ok(Exact(FBig::new(Repr::neg_infinity(), *self)));
274 }
275 if x.sign() == Sign::Negative {
276 return Err(FpError::OutOfDomain);
277 }
278 Ok(self.log2_internal(x, cache))
279 }
280
281 #[inline]
298 pub fn ln_1p<const B: Word>(
299 &self,
300 x: &Repr<B>,
301 cache: Option<&mut ConstCache>,
302 ) -> FpResult<FBig<R, B>> {
303 if x.is_infinite() {
304 return Err(FpError::InfiniteInput);
305 }
306 if x.sign() == Sign::Negative && !x.significand.is_zero() {
308 match FBig::<R, B>::new(x.clone(), *self).abs_cmp(&FBig::ONE) {
309 Ordering::Greater => return Err(FpError::OutOfDomain), Ordering::Equal => return Ok(Exact(FBig::new(Repr::neg_infinity(), *self))),
311 _ => {}
312 }
313 }
314 Ok(self.ln_internal(x, true, cache))
315 }
316
317 fn ln_internal<const B: Word>(
318 &self,
319 x: &Repr<B>,
320 one_plus: bool,
321 mut cache: Option<&mut ConstCache>,
322 ) -> Rounded<FBig<R, B>> {
323 assert_finite(x);
324
325 if !one_plus && x.is_one() {
329 return Exact(FBig::ZERO); }
331 if one_plus && x.significand.is_zero() {
332 let zero = if x.is_neg_zero() {
334 FBig::new(Repr::neg_zero(), *self)
335 } else {
336 FBig::ZERO
337 };
338 return Exact(zero);
339 }
340
341 assert_limited_precision(self.precision);
342
343 let guard_digits = ceil_usize(self.precision.log2_est() / B.log2_est()) + 2;
347 let mut work_precision = self.precision + guard_digits + one_plus as usize;
348 let context = Context::<R>::new(work_precision);
349 let x = FBig::new(context.repr_round_ref(x).value(), context);
350
351 let no_scaling = one_plus && x.log2_est() < -B.log2_est();
353
354 let (s, mut x_scaled) = if no_scaling {
355 (0, x)
356 } else {
357 let x = if one_plus { x + FBig::ONE } else { x };
358
359 let log2 = x.log2_bounds().0;
360 let s = log2 as isize - (log2 < 0.) as isize; let x_scaled = if B == 2 {
363 x >> s
364 } else if s > 0 {
365 x / (IBig::ONE << s as usize)
366 } else {
367 x * (IBig::ONE << (-s) as usize)
368 };
369 debug_assert!(x_scaled >= FBig::<R, B>::ONE);
370 (s, x_scaled)
371 };
372
373 if s < 0 || x_scaled.repr.sign() == Sign::Negative {
374 work_precision += self.precision;
377 x_scaled.context.precision = work_precision;
378 };
379 let work_context = Context::new(work_precision);
380
381 let z = if no_scaling {
385 let d = &x_scaled + (FBig::ONE + FBig::ONE);
386 x_scaled / d
387 } else {
388 (&x_scaled - FBig::ONE) / (x_scaled + FBig::ONE)
389 };
390 let z2 = z.sqr();
391 let mut pow = z.clone();
392 let mut sum = z;
393
394 let mut k: usize = 3;
395 loop {
396 pow *= &z2;
397
398 let increase = &pow / work_context.convert_int::<B>(k.into()).value();
399 if increase.abs_cmp(&sum.sub_ulp()).is_le() {
400 break;
401 }
402
403 sum += increase;
404 k += 2;
405 }
406
407 let result: FBig<R, B> = if no_scaling {
409 2 * sum
410 } else {
411 2 * sum + s * work_context.ln2::<B>(reborrow_cache(&mut cache))
412 };
413 result.with_precision(self.precision)
414 }
415
416 fn log2_internal<const B: Word>(
424 &self,
425 x: &Repr<B>,
426 mut cache: Option<&mut ConstCache>,
427 ) -> Rounded<FBig<R, B>> {
428 assert_finite(x);
429 if x.is_one() {
430 return Exact(FBig::ZERO); }
432
433 let guard = ceil_usize(self.precision.log2_est() / B.log2_est()) + 3;
434 let work = Context::<R>::new(self.precision + guard);
435 let ln_x = work
436 .ln_internal(x, false, reborrow_cache(&mut cache))
437 .value();
438 let ln2 = work.ln2::<B>(reborrow_cache(&mut cache));
439 (ln_x / ln2).with_precision(self.precision)
440 }
441}
442
443#[cfg(test)]
444mod tests {
445 use super::*;
446 use crate::round::mode;
447
448 #[test]
449 fn test_ln_zero_is_neg_infinity() {
450 let ctx = Context::<mode::HalfEven>::new(53);
451 let r = ctx.ln::<2>(&Repr::<2>::zero(), None).unwrap().value();
452 assert!(r.repr().is_infinite());
453 assert_eq!(r.repr().sign(), Sign::Negative);
454 }
455
456 #[test]
457 fn test_log2_domain() {
458 let ctx = Context::<mode::HalfEven>::new(53);
459 let r = ctx.log2::<2>(&Repr::<2>::zero(), None).unwrap().value();
461 assert!(r.repr().is_infinite());
462 assert_eq!(r.repr().sign(), Sign::Negative);
463 assert_eq!(ctx.log2::<2>(&Repr::new(IBig::from(-1), 0), None), Err(FpError::OutOfDomain));
465 }
466
467 #[test]
468 fn test_log2_powers_of_two() {
469 let ctx = Context::<mode::HalfEven>::new(53);
475 for k in [0usize, 1, 2, 3, 10, 50, 100, 200, 1000] {
476 let x = Repr::new(IBig::from(1) << k, 0); let r = ctx.log2::<2>(&x, None).unwrap();
478 let expected = ctx.convert_int::<2>(IBig::from(k)).value();
479 assert_eq!(r.value(), expected, "log2(2^{k}) = {k}");
480 }
481 assert!(matches!(ctx.log2::<2>(&Repr::new(IBig::from(1), 0), None).unwrap(), Exact(_)));
483 }
484
485 #[test]
486 fn test_log2_correctly_rounded() {
487 let p = 30;
491 let ctx = Context::<mode::HalfEven>::new(p);
492 let hi = Context::<mode::HalfEven>::new(200);
493
494 let cases_base2 = [
495 Repr::new(IBig::from(3), 0),
496 Repr::new(IBig::from(10), 0),
497 Repr::new(IBig::from(1), -24), Repr::new(IBig::from(1) << 50, 0), Repr::new(IBig::from(1), -50), ];
501 for x in cases_base2 {
502 let got = ctx.log2::<2>(&x, None).unwrap().value();
503 let want = (hi.ln::<2>(&x, None).unwrap().value() / hi.ln2::<2>(None))
504 .with_precision(p)
505 .value();
506 assert_eq!(got, want, "base-2 log2 mismatch for x={x:?}");
507 }
508
509 let x = Repr::new(IBig::from(123456), 0);
511 let got = ctx.log2::<10>(&x, None).unwrap().value();
512 let want = (hi.ln::<10>(&x, None).unwrap().value() / hi.ln2::<10>(None))
513 .with_precision(p)
514 .value();
515 assert_eq!(got, want, "base-10 log2 mismatch");
516 }
517
518 #[test]
519 fn test_log2_directed_bounds() {
520 use crate::round::mode::{Down, Up};
523 let p = 20;
524 let mid = Context::<mode::HalfEven>::new(p);
525 for x in [Repr::new(IBig::from(10), 0), Repr::new(IBig::from(3), 13)] {
526 let down = Context::<Down>::new(p).log2::<2>(&x, None).unwrap().value();
527 let up = Context::<Up>::new(p).log2::<2>(&x, None).unwrap().value();
528 let half = mid.log2::<2>(&x, None).unwrap().value();
529 assert!(down <= half, "down <= half failed for x={x:?}: {down:?} vs {half:?}");
530 assert!(half <= up, "half <= up failed for x={x:?}: {half:?} vs {up:?}");
531 assert!(down <= up, "down <= up failed for x={x:?}");
532 }
533 }
534
535 #[test]
536 fn test_iacoth() {
537 let context = Context::<mode::Zero>::new(10);
538 let binary_6 = context.iacoth::<2>(6.into()).with_precision(10).value();
539 assert_eq!(binary_6.repr.significand, IBig::from(689));
540 let decimal_6 = context.iacoth::<10>(6.into()).with_precision(10).value();
541 assert_eq!(decimal_6.repr.significand, IBig::from(1682361183));
542
543 let context = Context::<mode::Zero>::new(40);
544 let decimal_6 = context.iacoth::<10>(6.into()).with_precision(40).value();
545 assert_eq!(
546 decimal_6.repr.significand,
547 IBig::from_str_radix("1682361183106064652522967051084960450557", 10).unwrap()
548 );
549
550 let context = Context::<mode::Zero>::new(201);
551 let binary_6 = context.iacoth::<2>(6.into()).with_precision(201).value();
552 assert_eq!(
553 binary_6.repr.significand,
554 IBig::from_str_radix(
555 "2162760151454160450909229890833066944953539957685348083415205",
556 10
557 )
558 .unwrap()
559 );
560 }
561
562 #[test]
563 fn test_ln2_ln10() {
564 let context = Context::<mode::Zero>::new(45);
565 let decimal_ln2 = context.ln2::<10>(None).with_precision(45).value();
566 assert_eq!(
567 decimal_ln2.repr.significand,
568 IBig::from_str_radix("693147180559945309417232121458176568075500134", 10).unwrap()
569 );
570 let decimal_ln10 = context.ln10::<10>(None).with_precision(45).value();
571 assert_eq!(
572 decimal_ln10.repr.significand,
573 IBig::from_str_radix("230258509299404568401799145468436420760110148", 10).unwrap()
574 );
575
576 let context = Context::<mode::Zero>::new(180);
577 let binary_ln2 = context.ln2::<2>(None).with_precision(180).value();
578 assert_eq!(
579 binary_ln2.repr.significand,
580 IBig::from_str_radix("1062244963371879310175186301324412638028404515790072203", 10)
581 .unwrap()
582 );
583 let binary_ln10 = context.ln10::<2>(None).with_precision(180).value();
584 assert_eq!(
585 binary_ln10.repr.significand,
586 IBig::from_str_radix("882175346869410758689845931257775553286341791676474847", 10)
587 .unwrap()
588 );
589 }
590}