malachite_float/float/arithmetic/log_base_rational_float_base.rs
1// Copyright © 2026 Mikhail Hogrefe
2//
3// This file is part of Malachite.
4//
5// Malachite is free software: you can redistribute it and/or modify it under the terms of the GNU
6// Lesser General Public License (LGPL) as published by the Free Software Foundation; either version
7// 3 of the License, or (at your option) any later version. See <https://www.gnu.org/licenses/>.
8
9use crate::InnerFloat::{Infinity, NaN};
10use crate::float::arithmetic::log_base::{
11 dyadic_primitive_root, odd_significand_and_exponent, rational_value_log_of_dyadic_root,
12};
13use crate::float::arithmetic::log_base_2::extended_log_base_2_of_rational;
14use crate::float::basic::extended::ExtendedFloat;
15use crate::{Float, emulate_float_to_float_fn, float_infinity, float_nan, float_negative_infinity};
16use core::cmp::Ordering::{self, *};
17use malachite_base::num::arithmetic::traits::CeilingLogBase2;
18use malachite_base::num::basic::floats::PrimitiveFloat;
19use malachite_base::num::basic::integers::PrimitiveInt;
20use malachite_base::num::basic::traits::{NegativeZero, Zero as ZeroTrait};
21use malachite_base::num::conversion::traits::{ExactFrom, RoundingFrom};
22use malachite_base::rounding_modes::RoundingMode::{self, *};
23use malachite_nz::natural::arithmetic::float::round::float_can_round;
24use malachite_nz::platform::Limb;
25use malachite_q::Rational;
26
27// Returns `Some(log_base(x))` when it is rational, and `None` when it is irrational. The input `x`
28// must be a positive [`Rational`] not equal to 1, and `base` a finite positive [`Float`] not equal
29// to 1.
30//
31// `log_base(x)` is rational exactly when `x` and `base` are commensurable. `base` is dyadic, so
32// this reuses `rational_log_base_rational_rational_base` on the two `Rational`s; for a base in (0,
33// 1) -- where `Rational::checked_log_base` requires a base above 1 -- the identity `log_b(x) =
34// -log_{1/b}(x)` reduces to a base above 1. Balloon-safe via the `64 * prec` size bound.
35pub(crate) fn log_base_rational_float_base_rational(
36 x: &Rational,
37 base: &Float,
38) -> Option<Rational> {
39 // The base is dyadic, so its primitive root comes from its odd significand and exponent without
40 // materializing it (its exponent may be extreme, making the integer form enormous even though
41 // the Float is small). No size cutoff: skipping the check when the result is exactly
42 // representable would leave the Ziv loop unable to terminate.
43 let (s_b, t_b) = odd_significand_and_exponent(base);
44 let (z, h, e_base) = dyadic_primitive_root(&s_b, t_b);
45 let m = rational_value_log_of_dyadic_root(x, z, &h)?;
46 Some(Rational::from_signeds(m, i64::exact_from(e_base)))
47}
48
49// The computation of log_base(x) for a `Rational` `x` and a `Float` base is done by log_base(x) =
50// log_2(x) / log_2(base). The inputs are a positive `Rational` `x` not equal to 1 and a finite
51// positive `Float` base not equal to 1.
52//
53// `log_2(x)` is computed in the extended exponent range (`extended_log_base_2_of_rational`) so that
54// an `x` near 1 -- a `Rational` can be arbitrarily close, making `log_2(x)` underflow an ordinary
55// `Float` -- is represented faithfully (this is the underflow source). `log_2(base)` is an ordinary
56// native `Float` log (a `Float` base cannot be close enough to 1 to underflow its `log_2` at
57// practical precision; a base near 1 is instead the overflow source, where `log_2(base)` is tiny
58// and the quotient is huge). Both are wrapped as `ExtendedFloat`s, divided in the extended range,
59// and converted back with a single `into_float_helper` clamp. A base in (0, 1) gives a negative
60// `log_2(base)`, so the division yields the (sign-flipped) result for free.
61fn log_base_rational_float_base_normal(
62 x: &Rational,
63 base: &Float,
64 prec: u64,
65 rm: RoundingMode,
66) -> (Float, Ordering) {
67 // log_base(1) = 0, with the sign of 1 / log_2(base): positive for base > 1, negative for a base
68 // in (0, 1).
69 if *x == 1u32 {
70 return if *base < 1u32 {
71 (Float::NEGATIVE_ZERO, Equal)
72 } else {
73 (Float::ZERO, Equal)
74 };
75 }
76 // If log_base(x) is rational -- x and base commensurable -- compute it directly.
77 if let Some(q) = log_base_rational_float_base_rational(x, base) {
78 return Float::from_rational_prec_round(q, prec, rm);
79 }
80 // The result is irrational, so it is never exactly representable.
81 assert_ne!(rm, Exact, "Inexact log_base_rational_float_base");
82 // The initial slack keeps working_prec at least 7, so the working_prec - 6 below stays
83 // positive.
84 let mut working_prec = prec + 6 + prec.ceiling_log_base_2();
85 let mut increment = Limb::WIDTH;
86 loop {
87 // log_2(x), extended (handles an x near 1 without underflow); finite and nonzero (x is
88 // positive and not 1).
89 let num = extended_log_base_2_of_rational(x, working_prec);
90 // log_2(base), correctly rounded and wrapped; finite and nonzero (base positive and not 1).
91 let den = ExtendedFloat::from(base.log_base_2_prec_ref(working_prec).0);
92 // log_2(x) / log_2(base) in the extended range; cannot overflow or underflow here.
93 let quotient = num.div_prec_val_ref(&den, working_prec).0;
94 // log_2(x) is within 2 ulps, log_2(base) is correctly rounded (<= 1/2 ulp), and the
95 // division adds at most 1 more, for under 4 ulps total; working_prec - 6 correct bits
96 // comfortably suffice for the rounding test.
97 if float_can_round(
98 quotient.x.significand_ref().unwrap(),
99 working_prec - 6,
100 prec,
101 rm,
102 ) {
103 // Round the mantissa to prec, then place the extended exponent, clamping once to the
104 // Float range as the rounding mode dictates.
105 let (rounded, o) = Float::from_float_prec_round(quotient.x, prec, rm);
106 let mut result = ExtendedFloat::from(rounded);
107 result.exp = result.exp.checked_add(quotient.exp).unwrap();
108 return result.into_float_helper(prec, rm, o);
109 }
110 // Increase the precision.
111 working_prec += increment;
112 increment = working_prec >> 1;
113 }
114}
115
116// Computes log_base(x) = ln(x) / ln(base) for a `Rational` `x` and a `Float` base, following IEEE
117// division of the natural logs for every special case (so the function is total: no input value
118// panics). `x` is always finite.
119fn log_base_rational_float_base_helper(
120 x: &Rational,
121 base: &Float,
122 prec: u64,
123 rm: RoundingMode,
124) -> (Float, Ordering) {
125 // ln(base) is NaN for a NaN or negative base (negative finite or -infinity), and ln(x) is NaN
126 // for a negative x.
127 if base.is_nan() || *base < 0u32 || *x < 0u32 {
128 return (float_nan!(), Equal);
129 }
130 if base.is_infinite() {
131 // ln(base) = +infinity. ln(x) / +infinity = 0 for x > 0 (NaN for x = 0): +0 for x >= 1, -0
132 // for 0 < x < 1.
133 if *x == 0u32 {
134 return (float_nan!(), Equal);
135 }
136 return if *x < 1u32 {
137 (Float::NEGATIVE_ZERO, Equal)
138 } else {
139 (Float::ZERO, Equal)
140 };
141 }
142 if *base == 0u32 {
143 // ln(base) = -infinity. ln(x) / -infinity = 0 for x > 0 (NaN for x = 0), sign-flipped: -0
144 // for x >= 1, +0 for 0 < x < 1.
145 if *x == 0u32 {
146 return (float_nan!(), Equal);
147 }
148 return if *x < 1u32 {
149 (Float::ZERO, Equal)
150 } else {
151 (Float::NEGATIVE_ZERO, Equal)
152 };
153 }
154 if *base == 1u32 {
155 // ln(base) = +0. ln(x) / +0 = +-infinity by the sign of ln(x), or NaN for ln(x) = +0.
156 if *x == 0u32 {
157 return (float_negative_infinity!(), Equal); // ln(0) = -inf
158 }
159 return match x.partial_cmp(&1u32).unwrap() {
160 Equal => (float_nan!(), Equal), // +0 / +0
161 Greater => (float_infinity!(), Equal),
162 Less => (float_negative_infinity!(), Equal),
163 };
164 }
165 // base is positive finite and not 1.
166 if *x == 0u32 {
167 // ln(0) = -infinity. -infinity / ln(base): -infinity for base > 1, +infinity for base < 1.
168 return if *base < 1u32 {
169 (float_infinity!(), Equal)
170 } else {
171 (float_negative_infinity!(), Equal)
172 };
173 }
174 // x is a positive Rational and base is positive finite and not 1.
175 log_base_rational_float_base_normal(x, base, prec, rm)
176}
177
178impl Float {
179 /// Computes $\log_b x$, where $x$ is a [`Rational`] and the base $b$ is a [`Float`], returning
180 /// a [`Float`] rounded to the specified precision and with the specified rounding mode. Both
181 /// are taken by value. An [`Ordering`] is also returned, indicating whether the rounded value
182 /// is less than, equal to, or greater than the exact value. Although `NaN`s are not comparable
183 /// to any [`Float`], whenever this function returns a `NaN` it also returns `Equal`.
184 ///
185 /// The base may be any [`Float`]: the function is defined as $\ln x / \ln b$ for every
186 /// [`Rational`] $x$ and [`Float`] $b$, applying IEEE division to the natural logs, and never
187 /// panics on an input value. In particular a base in $(0,1)$ gives a (sign-flipped) logarithm,
188 /// and the non-normal and degenerate bases follow the limits below.
189 ///
190 /// This computes $\log_2 x / \log_2 b$, evaluating $\log_2 x$ in an extended exponent range (so
191 /// an $x$ near 1 does not lose accuracy) and wrapping the quotient so it may overflow (base
192 /// near 1) or underflow (x near 1) and be clamped exactly once.
193 ///
194 /// See [`RoundingMode`] for a description of the possible rounding modes.
195 ///
196 /// $$
197 /// f(x,b,p,m) = \log_b x+\varepsilon.
198 /// $$
199 /// - If $\log_b x$ is infinite, zero, or `NaN`, $\varepsilon$ may be ignored or assumed to be
200 /// 0.
201 /// - If $\log_b x$ is finite and nonzero, and $m$ is not `Nearest`, then $|\varepsilon| <
202 /// 2^{\lfloor\log_2 |\log_b x|\rfloor-p+1}$.
203 /// - If $\log_b x$ is finite and nonzero, and $m$ is `Nearest`, then $|\varepsilon| \leq
204 /// 2^{\lfloor\log_2 |\log_b x|\rfloor-p}$.
205 ///
206 /// If the output has a precision, it is `prec`.
207 ///
208 /// Special cases (with $b$ the base):
209 /// - $f(x,\text{NaN},p,m)=\text{NaN}$
210 /// - $f(x,b,p,m)=\text{NaN}$ for $x<0$ or $b<0$ (including $b=-\infty$)
211 /// - $f(0,b,p,m)=-\infty$ for $b>1$, and $\infty$ for $0<b<1$ (and $\text{NaN}$ for
212 /// $b\in\{\infty,\pm0.0\}$)
213 /// - $f(1,b,p,m)=0$ (with the sign of $1/\ln b$)
214 /// - $f(x,\infty,p,m)=0$ for $x>0$ (and $\text{NaN}$ for $x=0$)
215 /// - $f(x,\pm0.0,p,m)=0$ for $x>0$ (and $\text{NaN}$ for $x=0$)
216 /// - $f(x,1.0,p,m)=\infty$ for $x>1$, $-\infty$ for $0\leq x<1$, and $\text{NaN}$ for $x=1$
217 /// - $f(g^a,g^e,p,m)=a/e$ for a common rational $g$, rounded to precision $p$; the result is
218 /// exact if and only if $a/e$ is representable with precision $p$ (for example $\log_4
219 /// 8=3/2$)
220 ///
221 /// This function can both overflow (for a base near 1) and underflow (for an $x$ near 1).
222 ///
223 /// # Worst-case complexity
224 /// $T(n, m) = O(n (\log n)^2 \log\log n + m \log m \log\log m)$
225 ///
226 /// $M(n, m) = O(n \log n + m \log m)$
227 ///
228 /// where $T$ is time, $M$ is additional memory, $n$ is `prec`, and $m$ is
229 /// `max(x.significant_bits(), base.significant_bits())`.
230 ///
231 /// # Panics
232 /// Panics if `prec` is zero, or if `rm` is `Exact` but the result cannot be represented exactly
233 /// with the given precision.
234 ///
235 /// # Examples
236 /// ```
237 /// use malachite_base::rounding_modes::RoundingMode::*;
238 /// use malachite_float::Float;
239 /// use malachite_q::Rational;
240 /// use std::cmp::Ordering::*;
241 ///
242 /// let (log, o) = Float::log_base_rational_float_base_prec_round(
243 /// Rational::from(8),
244 /// Float::from(4),
245 /// 10,
246 /// Exact,
247 /// );
248 /// assert_eq!(log.to_string(), "1.5000"); // log_4(8) = 3/2
249 /// assert_eq!(o, Equal);
250 ///
251 /// let (log, o) = Float::log_base_rational_float_base_prec_round(
252 /// Rational::from(4),
253 /// Float::from(0.5),
254 /// 10,
255 /// Exact,
256 /// );
257 /// assert_eq!(log.to_string(), "-2.0000"); // log_{1/2}(4) = -2
258 /// assert_eq!(o, Equal);
259 /// ```
260 #[allow(clippy::needless_pass_by_value)]
261 #[inline]
262 pub fn log_base_rational_float_base_prec_round(
263 x: Rational,
264 base: Self,
265 prec: u64,
266 rm: RoundingMode,
267 ) -> (Self, Ordering) {
268 Self::log_base_rational_float_base_prec_round_ref(&x, &base, prec, rm)
269 }
270
271 /// Computes $\log_b x$, where $x$ is a [`Rational`] and the base $b$ is a [`Float`], returning
272 /// a [`Float`] rounded to the specified precision and with the specified rounding mode. Both
273 /// are taken by reference. An [`Ordering`] is also returned, indicating whether the rounded
274 /// value is less than, equal to, or greater than the exact value.
275 ///
276 /// See [`Float::log_base_rational_float_base_prec_round`] for details, special cases, and a
277 /// description of the rounding behavior.
278 ///
279 /// # Worst-case complexity
280 /// $T(n, m) = O(n (\log n)^2 \log\log n + m \log m \log\log m)$
281 ///
282 /// $M(n, m) = O(n \log n + m \log m)$
283 ///
284 /// where $T$ is time, $M$ is additional memory, $n$ is `prec`, and $m$ is
285 /// `max(x.significant_bits(), base.significant_bits())`.
286 ///
287 /// # Panics
288 /// Panics if `prec` is zero, or if `rm` is `Exact` but the result cannot be represented exactly
289 /// with the given precision.
290 ///
291 /// # Examples
292 /// ```
293 /// use malachite_base::rounding_modes::RoundingMode::*;
294 /// use malachite_float::Float;
295 /// use malachite_q::Rational;
296 /// use std::cmp::Ordering::*;
297 ///
298 /// let (log, o) = Float::log_base_rational_float_base_prec_round_ref(
299 /// &Rational::from(9),
300 /// &Float::from(3),
301 /// 10,
302 /// Exact,
303 /// );
304 /// assert_eq!(log.to_string(), "2.0000"); // log_3(9) = 2
305 /// assert_eq!(o, Equal);
306 ///
307 /// let (log, o) = Float::log_base_rational_float_base_prec_round_ref(
308 /// &Rational::from_signeds(1, 3),
309 /// &Float::from(3),
310 /// 10,
311 /// Exact,
312 /// );
313 /// assert_eq!(log.to_string(), "-1.0000"); // log_3(1/3) = -1
314 /// assert_eq!(o, Equal);
315 /// ```
316 pub fn log_base_rational_float_base_prec_round_ref(
317 x: &Rational,
318 base: &Self,
319 prec: u64,
320 rm: RoundingMode,
321 ) -> (Self, Ordering) {
322 assert_ne!(prec, 0);
323 log_base_rational_float_base_helper(x, base, prec, rm)
324 }
325
326 /// Computes $\log_b x$, where $x$ is a [`Rational`] and the base $b$ is a [`Float`], returning
327 /// a [`Float`] rounded to the nearest value of the specified precision. Both are taken by
328 /// value. An [`Ordering`] is also returned.
329 ///
330 /// See [`Float::log_base_rational_float_base_prec_round`] for details and special cases.
331 ///
332 /// # Worst-case complexity
333 /// $T(n, m) = O(n (\log n)^2 \log\log n + m \log m \log\log m)$
334 ///
335 /// $M(n, m) = O(n \log n + m \log m)$
336 ///
337 /// where $T$ is time, $M$ is additional memory, $n$ is `prec`, and $m$ is
338 /// `max(x.significant_bits(), base.significant_bits())`.
339 ///
340 /// # Panics
341 /// Panics if `prec` is zero.
342 ///
343 /// # Examples
344 /// ```
345 /// use malachite_float::Float;
346 /// use malachite_q::Rational;
347 /// use std::cmp::Ordering::*;
348 ///
349 /// let (log, o) =
350 /// Float::log_base_rational_float_base_prec(Rational::from(8), Float::from(4), 10);
351 /// assert_eq!(log.to_string(), "1.5000"); // log_4(8) = 3/2
352 /// assert_eq!(o, Equal);
353 /// ```
354 #[allow(clippy::needless_pass_by_value)]
355 #[inline]
356 pub fn log_base_rational_float_base_prec(
357 x: Rational,
358 base: Self,
359 prec: u64,
360 ) -> (Self, Ordering) {
361 Self::log_base_rational_float_base_prec_round_ref(&x, &base, prec, Nearest)
362 }
363
364 /// Computes $\log_b x$, where $x$ is a [`Rational`] and the base $b$ is a [`Float`], returning
365 /// a [`Float`] rounded to the nearest value of the specified precision. Both are taken by
366 /// reference. An [`Ordering`] is also returned.
367 ///
368 /// See [`Float::log_base_rational_float_base_prec_round`] for details and special cases.
369 ///
370 /// # Worst-case complexity
371 /// $T(n, m) = O(n (\log n)^2 \log\log n + m \log m \log\log m)$
372 ///
373 /// $M(n, m) = O(n \log n + m \log m)$
374 ///
375 /// where $T$ is time, $M$ is additional memory, $n$ is `prec`, and $m$ is
376 /// `max(x.significant_bits(), base.significant_bits())`.
377 ///
378 /// # Panics
379 /// Panics if `prec` is zero.
380 ///
381 /// # Examples
382 /// ```
383 /// use malachite_float::Float;
384 /// use malachite_q::Rational;
385 /// use std::cmp::Ordering::*;
386 ///
387 /// let (log, o) =
388 /// Float::log_base_rational_float_base_prec_ref(&Rational::from(9), &Float::from(3), 10);
389 /// assert_eq!(log.to_string(), "2.0000"); // log_3(9) = 2
390 /// assert_eq!(o, Equal);
391 /// ```
392 #[inline]
393 pub fn log_base_rational_float_base_prec_ref(
394 x: &Rational,
395 base: &Self,
396 prec: u64,
397 ) -> (Self, Ordering) {
398 Self::log_base_rational_float_base_prec_round_ref(x, base, prec, Nearest)
399 }
400}
401
402/// Computes $\log_b x$, the base-$b$ logarithm of a [`Rational`], where the base $b$ is a primitive
403/// float, returning a primitive float result. Using this function is more accurate than computing
404/// the logarithm using the standard library, whose logarithm functions are not always correctly
405/// rounded.
406///
407/// Unlike the integer- and rational-base logarithms, the base may be any primitive float: the
408/// function is defined as $\ln x / \ln b$ and never panics on an input value. A base in $(0,1)$
409/// gives a (sign-flipped) logarithm, and the non-normal and degenerate bases follow the limits
410/// below.
411///
412/// $$
413/// f(x,b) = \log_b x+\varepsilon.
414/// $$
415/// - If $\log_b x$ is infinite, zero, or `NaN`, $\varepsilon$ may be ignored or assumed to be 0.
416/// - If $\log_b x$ is finite and nonzero, then $|\varepsilon| < 2^{\lfloor\log_2 |\log_b
417/// x|\rfloor-p}$, where $p$ is precision of the output (typically 24 if `T` is a [`f32`] and 53
418/// if `T` is a [`f64`], but less if the output is subnormal).
419///
420/// Special cases (with $b$ the base):
421/// - $f(x,\text{NaN})=\text{NaN}$
422/// - $f(x,b)=\text{NaN}$ for $x<0$ or $b<0$ (including $b=-\infty$)
423/// - $f(0,b)=-\infty$ for $b>1$, and $\infty$ for $0<b<1$ (and $\text{NaN}$ for
424/// $b\in\{\infty,\pm0.0\}$)
425/// - $f(1,b)=0.0$ (with the sign of $1/\ln b$)
426/// - $f(x,\infty)=0.0$ for $x>0$ (and $\text{NaN}$ for $x=0$)
427/// - $f(x,\pm0.0)=0.0$ for $x>0$ (and $\text{NaN}$ for $x=0$)
428/// - $f(x,1.0)=\infty$ for $x>1$, $-\infty$ for $0\leq x<1$, and $\text{NaN}$ for $x=1$
429///
430/// This function can both overflow (for a base near 1) and underflow (for an $x$ near 1).
431///
432/// # Worst-case complexity
433/// $T(m) = O(m \log m \log\log m)$
434///
435/// $M(m) = O(m \log m)$
436///
437/// where $T$ is time, $M$ is additional memory, and $m$ is `x.significant_bits()`.
438///
439/// # Examples
440/// ```
441/// use malachite_base::num::basic::traits::NegativeOne;
442/// use malachite_base::num::float::NiceFloat;
443/// use malachite_float::float::arithmetic::log_base_rational_float_base::*;
444/// use malachite_q::Rational;
445///
446/// // log_4(8) = 3/2
447/// assert_eq!(
448/// NiceFloat(primitive_float_log_base_rational_float_base::<f32>(
449/// &Rational::from(8),
450/// 4.0
451/// )),
452/// NiceFloat(1.5)
453/// );
454/// // log_(1/2)(4) = -2
455/// assert_eq!(
456/// NiceFloat(primitive_float_log_base_rational_float_base::<f32>(
457/// &Rational::from(4),
458/// 0.5
459/// )),
460/// NiceFloat(-2.0)
461/// );
462/// // log_10(1/3)
463/// assert_eq!(
464/// NiceFloat(primitive_float_log_base_rational_float_base::<f32>(
465/// &Rational::from_unsigneds(1u8, 3),
466/// 10.0
467/// )),
468/// NiceFloat(-0.47712126)
469/// );
470/// assert!(
471/// primitive_float_log_base_rational_float_base::<f32>(&Rational::NEGATIVE_ONE, 10.0).is_nan()
472/// );
473/// assert!(
474/// primitive_float_log_base_rational_float_base::<f32>(&Rational::from(8), f32::NAN).is_nan()
475/// );
476/// ```
477#[inline]
478#[allow(clippy::type_repetition_in_bounds)]
479pub fn primitive_float_log_base_rational_float_base<T: PrimitiveFloat>(x: &Rational, base: T) -> T
480where
481 Float: From<T> + PartialOrd<T>,
482 for<'a> T: ExactFrom<&'a Float> + RoundingFrom<&'a Float>,
483{
484 emulate_float_to_float_fn(
485 |base2, prec| Float::log_base_rational_float_base_prec_ref(x, &base2, prec),
486 base,
487 )
488}