malachite_float/constants/log_2.rs
1// Copyright © 2025 Mikhail Hogrefe
2//
3// Uses code adopted from the GNU MPFR Library.
4//
5// Copyright 1999, 2001-2024 Free Software Foundation, Inc.
6//
7// Contributed by the AriC and Caramba projects, INRIA.
8//
9// This file is part of Malachite.
10//
11// Malachite is free software: you can redistribute it and/or modify it under the terms of the GNU
12// Lesser General Public License (LGPL) as published by the Free Software Foundation; either version
13// 3 of the License, or (at your option) any later version. See <https://www.gnu.org/licenses/>.
14
15use crate::Float;
16use alloc::vec;
17use core::cmp::Ordering;
18use core::mem::swap;
19use malachite_base::num::arithmetic::traits::CeilingLogBase2;
20use malachite_base::num::basic::integers::PrimitiveInt;
21use malachite_base::num::basic::traits::{One, Zero};
22use malachite_base::num::conversion::traits::WrappingFrom;
23use malachite_base::rounding_modes::RoundingMode::{self, *};
24use malachite_nz::integer::Integer;
25use malachite_nz::natural::arithmetic::float_extras::float_can_round;
26use malachite_nz::platform::Limb;
27
28// Auxiliary function: Compute the terms from n1 to n2 (excluded) 3 / 4 * sum((-1) ^ n * n! ^ 2 / 2
29// ^ n / (2 * n + 1)!, n = n1...n2 - 1).
30//
31// Numerator is T[0], denominator is Q[0], Compute P[0] only when need_P is non-zero.
32//
33// Need 1 + ceil(log(n2 - n1) / log(2)) cells in T[], P[], Q[].
34//
35// This is S from const_log2.c, MPFR 4.2.0.
36fn sum(t: &mut [Integer], p: &mut [Integer], q: &mut [Integer], n1: u64, n2: u64, need_p: bool) {
37 if n2 == n1 + 1 {
38 p[0] = if n1 == 0 {
39 const { Integer::const_from_unsigned(3) }
40 } else {
41 -Integer::from(n1)
42 };
43 q[0] = ((Integer::from(n1) << 1u32) + Integer::ONE) << 2u32;
44 t[0].clone_from(&p[0]);
45 } else {
46 let m = (n1 >> 1) + (n2 >> 1) + (n1 & 1 & n2);
47 sum(t, p, q, n1, m, true);
48 let (t_head, t_tail) = t.split_first_mut().unwrap();
49 let (p_head, p_tail) = p.split_first_mut().unwrap();
50 let (q_head, q_tail) = q.split_first_mut().unwrap();
51 sum(t_tail, p_tail, q_tail, m, n2, need_p);
52 *t_head *= &q_tail[0];
53 t_tail[0] *= &*p_head;
54 *t_head += &t_tail[0];
55 if need_p {
56 *p_head *= &p_tail[0];
57 }
58 *q_head *= &q_tail[0];
59 // remove common trailing zeros if any
60 let mut tz = t_head.trailing_zeros().unwrap();
61 if tz != 0 {
62 let mut qz = q_head.trailing_zeros().unwrap();
63 if qz < tz {
64 tz = qz;
65 }
66 if need_p {
67 qz = p_head.trailing_zeros().unwrap();
68 if qz < tz {
69 tz = qz;
70 }
71 }
72 // now tz = min(val(T), val(Q), val(P))
73 if tz != 0 {
74 *t_head >>= tz;
75 *q_head >>= tz;
76 if need_p {
77 *p_head >>= tz;
78 }
79 }
80 }
81 }
82}
83
84impl Float {
85 /// Returns an approximation to the natural logarithm of 2, with the given precision and rounded
86 /// using the given [`RoundingMode`]. An [`Ordering`] is also returned, indicating whether the
87 /// rounded value is less than or greater than the exact value of the constant. (Since the
88 /// constant is irrational, the rounded value is never equal to the exact value.)
89 ///
90 /// $$
91 /// L = \log 2.
92 /// $$
93 ///
94 /// The constant is irrational.
95 ///
96 /// The output has precision `prec`.
97 ///
98 /// # Worst-case complexity
99 /// $T(n) = O(n (\log n)^2 \log\log n)$
100 ///
101 /// $M(n) = O(n (\log n)^2)$
102 ///
103 /// where $T$ is time, $M$ is additional memory, and $n$ is `prec`.
104 ///
105 /// # Panics
106 /// Panics if `prec` is zero or if `rm` is `Exact`.
107 ///
108 /// # Examples
109 /// ```
110 /// use malachite_base::rounding_modes::RoundingMode::*;
111 /// use malachite_float::Float;
112 /// use std::cmp::Ordering::*;
113 ///
114 /// let (l2, o) = Float::log_2_prec_round(100, Floor);
115 /// assert_eq!(l2.to_string(), "0.693147180559945309417232121458");
116 /// assert_eq!(o, Less);
117 ///
118 /// let (l2, o) = Float::log_2_prec_round(100, Ceiling);
119 /// assert_eq!(l2.to_string(), "0.693147180559945309417232121459");
120 /// assert_eq!(o, Greater);
121 /// ```
122 ///
123 /// This is mpfr_const_log2_internal from const_log2.c, MPFR 4.2.0.
124 pub fn log_2_prec_round(prec: u64, rm: RoundingMode) -> (Self, Ordering) {
125 let mut working_prec = prec + prec.ceiling_log_base_2() + 3;
126 let mut increment = Limb::WIDTH;
127 loop {
128 let big_n = working_prec / 3 + 1;
129 // the following are needed for error analysis (see algorithms.tex)
130 assert!(working_prec >= 3 && big_n >= 2);
131 let lg_big_n = usize::wrapping_from(big_n.ceiling_log_base_2()) + 1;
132 let mut scratch = vec![Integer::ZERO; 3 * lg_big_n];
133 split_into_chunks_mut!(scratch, lg_big_n, [t, p], q);
134 sum(t, p, q, 0, big_n, false);
135 let mut t0 = Integer::ZERO;
136 let mut q0 = Integer::ZERO;
137 swap(&mut t0, &mut t[0]);
138 swap(&mut q0, &mut q[0]);
139 let log_2 = Self::from_integer_prec(t0, working_prec).0
140 / Self::from_integer_prec(q0, working_prec).0;
141 if float_can_round(log_2.significand_ref().unwrap(), working_prec - 2, prec, rm) {
142 return Self::from_float_prec_round(log_2, prec, rm);
143 }
144 working_prec += increment;
145 increment = working_prec >> 1;
146 }
147 }
148
149 /// Returns an approximation to the natural logarithm of 2, with the given precision and rounded
150 /// to the nearest [`Float`] of that precision. An [`Ordering`] is also returned, indicating
151 /// whether the rounded value is less than or greater than the exact value of the constant.
152 /// (Since the constant is irrational, the rounded value is never equal to the exact value.)
153 ///
154 /// $$
155 /// L = \log 2.
156 /// $$
157 ///
158 /// The constant is irrational.
159 ///
160 /// The output has precision `prec`.
161 ///
162 /// # Worst-case complexity
163 /// $T(n) = O(n (\log n)^2 \log\log n)$
164 ///
165 /// $M(n) = O(n (\log n)^2)$
166 ///
167 /// where $T$ is time, $M$ is additional memory, and $n$ is `prec`.
168 ///
169 /// # Panics
170 /// Panics if `prec` is zero.
171 ///
172 /// # Examples
173 /// ```
174 /// use malachite_float::Float;
175 /// use std::cmp::Ordering::*;
176 ///
177 /// let (l2, o) = Float::log_2_prec(1);
178 /// assert_eq!(l2.to_string(), "0.5");
179 /// assert_eq!(o, Less);
180 ///
181 /// let (l2, o) = Float::log_2_prec(10);
182 /// assert_eq!(l2.to_string(), "0.693");
183 /// assert_eq!(o, Greater);
184 ///
185 /// let (l2, o) = Float::log_2_prec(100);
186 /// assert_eq!(l2.to_string(), "0.693147180559945309417232121458");
187 /// assert_eq!(o, Less);
188 /// ```
189 #[inline]
190 pub fn log_2_prec(prec: u64) -> (Self, Ordering) {
191 Self::log_2_prec_round(prec, Nearest)
192 }
193}