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},
11 fbig::FBig,
12 repr::{Context, Repr, Word},
13 round::{Round, Rounded},
14};
15
16impl<const B: Word> EstimatedLog2 for Repr<B> {
17 fn log2_bounds(&self) -> (f32, f32) {
19 if self.significand.is_zero() {
20 return (f32::NEG_INFINITY, f32::NEG_INFINITY);
21 }
22
23 let (logs_lb, logs_ub) = self.significand.log2_bounds();
25 let (logb_lb, logb_ub) = if B.is_power_of_two() {
26 let log = B.trailing_zeros() as f32;
27 (log, log)
28 } else {
29 B.log2_bounds()
30 };
31 let e = self.exponent as f32;
32 let (lb, ub) = if self.exponent >= 0 {
33 (logs_lb + e * logb_lb, logs_ub + e * logb_ub)
34 } else {
35 (logs_lb + e * logb_ub, logs_ub + e * logb_lb)
36 };
37 (next_down(lb), next_up(ub))
38 }
39
40 fn log2_est(&self) -> f32 {
41 let logs = self.significand.log2_est();
42 let logb = if B.is_power_of_two() {
43 B.trailing_zeros() as f32
44 } else {
45 B.log2_est()
46 };
47 logs + self.exponent as f32 * logb
48 }
49}
50
51impl<R: Round, const B: Word> EstimatedLog2 for FBig<R, B> {
52 #[inline]
53 fn log2_bounds(&self) -> (f32, f32) {
54 self.repr.log2_bounds()
55 }
56
57 #[inline]
58 fn log2_est(&self) -> f32 {
59 self.repr.log2_est()
60 }
61}
62
63impl<R: Round, const B: Word> FBig<R, B> {
64 #[inline]
77 pub fn ln(&self) -> Self {
78 self.context.ln(&self.repr).value()
79 }
80
81 #[inline]
94 pub fn ln_1p(&self) -> Self {
95 self.context.ln_1p(&self.repr).value()
96 }
97}
98
99impl<R: Round> Context<R> {
100 #[inline]
104 fn ln2<const B: Word>(&self) -> FBig<R, B> {
105 4 * self.iacoth(6.into()) + 2 * self.iacoth(99.into())
109 }
110
111 #[inline]
115 fn ln10<const B: Word>(&self) -> FBig<R, B> {
116 3 * self.ln2() + 2 * self.iacoth(9.into())
118 }
119
120 #[inline]
124 pub(crate) fn ln_base<const B: Word>(&self) -> FBig<R, B> {
125 match B {
126 2 => self.ln2(),
127 10 => self.ln10(),
128 i if i.is_power_of_two() => self.ln2() * i.trailing_zeros(),
129 _ => self.ln(&Repr::new(Repr::<B>::BASE.into(), 0)).value(),
130 }
131 }
132
133 fn iacoth<const B: Word>(&self, n: IBig) -> FBig<R, B> {
138 let guard_digits = (self.precision.log2_est() / B.log2_est()) as usize;
161 let work_context = Self::new(self.precision + guard_digits + 2);
162
163 let n = work_context.convert_int(n).value();
164 let inv = FBig::ONE / n;
165 let inv2 = inv.sqr();
166 let mut sum = inv.clone();
167 let mut pow = inv;
168
169 let mut k: usize = 3;
170 loop {
171 pow *= &inv2;
172
173 let increase = &pow / work_context.convert_int::<B>(k.into()).value();
174 if increase < sum.sub_ulp() {
175 return sum;
176 }
177
178 sum += increase;
179 k += 2;
180 }
181 }
182
183 #[inline]
200 pub fn ln<const B: Word>(&self, x: &Repr<B>) -> Rounded<FBig<R, B>> {
201 self.ln_internal(x, false)
202 }
203
204 #[inline]
221 pub fn ln_1p<const B: Word>(&self, x: &Repr<B>) -> Rounded<FBig<R, B>> {
222 self.ln_internal(x, true)
223 }
224
225 fn ln_internal<const B: Word>(&self, x: &Repr<B>, one_plus: bool) -> Rounded<FBig<R, B>> {
226 assert_finite(x);
227 assert_limited_precision(self.precision);
228
229 if (one_plus && x.is_zero()) || (!one_plus && x.is_one()) {
230 return Exact(FBig::ZERO);
231 }
232
233 let guard_digits = (self.precision.log2_est() / B.log2_est()) as usize + 2;
237 let mut work_precision = self.precision + guard_digits + one_plus as usize;
238 let context = Context::<R>::new(work_precision);
239 let x = FBig::new(context.repr_round_ref(x).value(), context);
240
241 let no_scaling = one_plus && x.log2_est() < -B.log2_est();
243
244 let (s, mut x_scaled) = if no_scaling {
245 (0, x)
246 } else {
247 let x = if one_plus { x + FBig::ONE } else { x };
248
249 let log2 = x.log2_bounds().0;
250 let s = log2 as isize - (log2 < 0.) as isize; let x_scaled = if B == 2 {
253 x >> s
254 } else if s > 0 {
255 x / (IBig::ONE << s as usize)
256 } else {
257 x * (IBig::ONE << (-s) as usize)
258 };
259 debug_assert!(x_scaled >= FBig::<R, B>::ONE);
260 (s, x_scaled)
261 };
262
263 if s < 0 || x_scaled.repr.sign() == Sign::Negative {
264 work_precision += self.precision;
267 x_scaled.context.precision = work_precision;
268 };
269 let work_context = Context::new(work_precision);
270
271 let z = if no_scaling {
275 let d = &x_scaled + (FBig::ONE + FBig::ONE);
276 x_scaled / d
277 } else {
278 (&x_scaled - FBig::ONE) / (x_scaled + FBig::ONE)
279 };
280 let z2 = z.sqr();
281 let mut pow = z.clone();
282 let mut sum = z;
283
284 let mut k: usize = 3;
285 loop {
286 pow *= &z2;
287
288 let increase = &pow / work_context.convert_int::<B>(k.into()).value();
289 if increase.abs_cmp(&sum.sub_ulp()).is_le() {
290 break;
291 }
292
293 sum += increase;
294 k += 2;
295 }
296
297 let result: FBig<R, B> = if no_scaling {
299 2 * sum
300 } else {
301 2 * sum + s * work_context.ln2()
302 };
303 result.with_precision(self.precision)
304 }
305}
306
307#[cfg(test)]
308mod tests {
309 use super::*;
310 use crate::round::mode;
311
312 #[test]
313 fn test_iacoth() {
314 let context = Context::<mode::Zero>::new(10);
315 let binary_6 = context.iacoth::<2>(6.into()).with_precision(10).value();
316 assert_eq!(binary_6.repr.significand, IBig::from(689));
317 let decimal_6 = context.iacoth::<10>(6.into()).with_precision(10).value();
318 assert_eq!(decimal_6.repr.significand, IBig::from(1682361183));
319
320 let context = Context::<mode::Zero>::new(40);
321 let decimal_6 = context.iacoth::<10>(6.into()).with_precision(40).value();
322 assert_eq!(
323 decimal_6.repr.significand,
324 IBig::from_str_radix("1682361183106064652522967051084960450557", 10).unwrap()
325 );
326
327 let context = Context::<mode::Zero>::new(201);
328 let binary_6 = context.iacoth::<2>(6.into()).with_precision(201).value();
329 assert_eq!(
330 binary_6.repr.significand,
331 IBig::from_str_radix(
332 "2162760151454160450909229890833066944953539957685348083415205",
333 10
334 )
335 .unwrap()
336 );
337 }
338
339 #[test]
340 fn test_ln2_ln10() {
341 let context = Context::<mode::Zero>::new(45);
342 let decimal_ln2 = context.ln2::<10>().with_precision(45).value();
343 assert_eq!(
344 decimal_ln2.repr.significand,
345 IBig::from_str_radix("693147180559945309417232121458176568075500134", 10).unwrap()
346 );
347 let decimal_ln10 = context.ln10::<10>().with_precision(45).value();
348 assert_eq!(
349 decimal_ln10.repr.significand,
350 IBig::from_str_radix("230258509299404568401799145468436420760110148", 10).unwrap()
351 );
352
353 let context = Context::<mode::Zero>::new(180);
354 let binary_ln2 = context.ln2::<2>().with_precision(180).value();
355 assert_eq!(
356 binary_ln2.repr.significand,
357 IBig::from_str_radix("1062244963371879310175186301324412638028404515790072203", 10)
358 .unwrap()
359 );
360 let binary_ln10 = context.ln10::<2>().with_precision(180).value();
361 assert_eq!(
362 binary_ln10.repr.significand,
363 IBig::from_str_radix("882175346869410758689845931257775553286341791676474847", 10)
364 .unwrap()
365 );
366 }
367}