Skip to main content

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}