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_extras::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).
126    if base.is_nan() || *base < 0u32 {
127        return (float_nan!(), Equal);
128    }
129    // ln(x) is NaN for a negative x.
130    if *x < 0u32 {
131        return (float_nan!(), Equal);
132    }
133    if base.is_infinite() {
134        // ln(base) = +infinity. ln(x) / +infinity = 0 for x > 0 (NaN for x = 0): +0 for x >= 1, -0
135        // for 0 < x < 1.
136        if *x == 0u32 {
137            return (float_nan!(), Equal);
138        }
139        return if *x < 1u32 {
140            (Float::NEGATIVE_ZERO, Equal)
141        } else {
142            (Float::ZERO, Equal)
143        };
144    }
145    if *base == 0u32 {
146        // ln(base) = -infinity. ln(x) / -infinity = 0 for x > 0 (NaN for x = 0), sign-flipped: -0
147        // for x >= 1, +0 for 0 < x < 1.
148        if *x == 0u32 {
149            return (float_nan!(), Equal);
150        }
151        return if *x < 1u32 {
152            (Float::ZERO, Equal)
153        } else {
154            (Float::NEGATIVE_ZERO, Equal)
155        };
156    }
157    if *base == 1u32 {
158        // ln(base) = +0. ln(x) / +0 = +-infinity by the sign of ln(x), or NaN for ln(x) = +0.
159        if *x == 0u32 {
160            return (float_negative_infinity!(), Equal); // ln(0) = -inf
161        }
162        return match x.partial_cmp(&1u32).unwrap() {
163            Equal => (float_nan!(), Equal), // +0 / +0
164            Greater => (float_infinity!(), Equal),
165            Less => (float_negative_infinity!(), Equal),
166        };
167    }
168    // base is positive finite and not 1.
169    if *x == 0u32 {
170        // ln(0) = -infinity. -infinity / ln(base): -infinity for base > 1, +infinity for base < 1.
171        return if *base < 1u32 {
172            (float_infinity!(), Equal)
173        } else {
174            (float_negative_infinity!(), Equal)
175        };
176    }
177    // x is a positive Rational and base is positive finite and not 1.
178    log_base_rational_float_base_normal(x, base, prec, rm)
179}
180
181impl Float {
182    /// Computes $\log_b x$, where $x$ is a [`Rational`] and the base $b$ is a [`Float`], returning
183    /// a [`Float`] rounded to the specified precision and with the specified rounding mode. Both
184    /// are taken by value. An [`Ordering`] is also returned, indicating whether the rounded value
185    /// is less than, equal to, or greater than the exact value. Although `NaN`s are not comparable
186    /// to any [`Float`], whenever this function returns a `NaN` it also returns `Equal`.
187    ///
188    /// The base may be any [`Float`]: the function is defined as $\ln x / \ln b$ for every
189    /// [`Rational`] $x$ and [`Float`] $b$, applying IEEE division to the natural logs, and never
190    /// panics on an input value. In particular a base in $(0,1)$ gives a (sign-flipped) logarithm,
191    /// and the non-normal and degenerate bases follow the limits below.
192    ///
193    /// This computes $\log_2 x / \log_2 b$, evaluating $\log_2 x$ in an extended exponent range (so
194    /// an $x$ near 1 does not lose accuracy) and wrapping the quotient so it may overflow (base
195    /// near 1) or underflow (x near 1) and be clamped exactly once.
196    ///
197    /// See [`RoundingMode`] for a description of the possible rounding modes.
198    ///
199    /// $$
200    /// f(x,b,p,m) = \log_b x+\varepsilon.
201    /// $$
202    /// - If $\log_b x$ is infinite, zero, or `NaN`, $\varepsilon$ may be ignored or assumed to be
203    ///   0.
204    /// - If $\log_b x$ is finite and nonzero, and $m$ is not `Nearest`, then $|\varepsilon| <
205    ///   2^{\lfloor\log_2 |\log_b x|\rfloor-p+1}$.
206    /// - If $\log_b x$ is finite and nonzero, and $m$ is `Nearest`, then $|\varepsilon| \leq
207    ///   2^{\lfloor\log_2 |\log_b x|\rfloor-p}$.
208    ///
209    /// If the output has a precision, it is `prec`.
210    ///
211    /// Special cases (with $b$ the base):
212    /// - $f(x,\text{NaN},p,m)=\text{NaN}$
213    /// - $f(x,b,p,m)=\text{NaN}$ for $x<0$ or $b<0$ (including $b=-\infty$)
214    /// - $f(0,b,p,m)=-\infty$ for $b>1$, and $\infty$ for $0<b<1$ (and $\text{NaN}$ for
215    ///   $b\in\{\infty,\pm0.0\}$)
216    /// - $f(1,b,p,m)=0$ (with the sign of $1/\ln b$)
217    /// - $f(x,\infty,p,m)=0$ for $x>0$ (and $\text{NaN}$ for $x=0$)
218    /// - $f(x,\pm0.0,p,m)=0$ for $x>0$ (and $\text{NaN}$ for $x=0$)
219    /// - $f(x,1.0,p,m)=\infty$ for $x>1$, $-\infty$ for $0\leq x<1$, and $\text{NaN}$ for $x=1$
220    /// - $f(g^a,g^e,p,m)=a/e$ for a common rational $g$, rounded to precision $p$; the result is
221    ///   exact if and only if $a/e$ is representable with precision $p$ (for example $\log_4
222    ///   8=3/2$)
223    ///
224    /// This function can both overflow (for a base near 1) and underflow (for an $x$ near 1).
225    ///
226    /// # Worst-case complexity
227    /// $T(n) = O(n (\log n)^2 \log\log n)$
228    ///
229    /// $M(n) = O(n (\log n)^2)$
230    ///
231    /// where $T$ is time, $M$ is additional memory, and $n$ is `prec`.
232    ///
233    /// # Panics
234    /// Panics if `prec` is zero, or if `rm` is `Exact` but the result cannot be represented exactly
235    /// with the given precision.
236    ///
237    /// # Examples
238    /// ```
239    /// use malachite_base::rounding_modes::RoundingMode::*;
240    /// use malachite_float::Float;
241    /// use malachite_q::Rational;
242    /// use std::cmp::Ordering::*;
243    ///
244    /// let (log, o) = Float::log_base_rational_float_base_prec_round(
245    ///     Rational::from(8),
246    ///     Float::from(4),
247    ///     10,
248    ///     Exact,
249    /// );
250    /// assert_eq!(log.to_string(), "1.5000"); // log_4(8) = 3/2
251    /// assert_eq!(o, Equal);
252    ///
253    /// let (log, o) = Float::log_base_rational_float_base_prec_round(
254    ///     Rational::from(4),
255    ///     Float::from(0.5),
256    ///     10,
257    ///     Exact,
258    /// );
259    /// assert_eq!(log.to_string(), "-2.0000"); // log_{1/2}(4) = -2
260    /// assert_eq!(o, Equal);
261    /// ```
262    #[allow(clippy::needless_pass_by_value)]
263    #[inline]
264    pub fn log_base_rational_float_base_prec_round(
265        x: Rational,
266        base: Self,
267        prec: u64,
268        rm: RoundingMode,
269    ) -> (Self, Ordering) {
270        Self::log_base_rational_float_base_prec_round_ref(&x, &base, prec, rm)
271    }
272
273    /// Computes $\log_b x$, where $x$ is a [`Rational`] and the base $b$ is a [`Float`], returning
274    /// a [`Float`] rounded to the specified precision and with the specified rounding mode. Both
275    /// are taken by reference. An [`Ordering`] is also returned, indicating whether the rounded
276    /// value is less than, equal to, or greater than the exact value.
277    ///
278    /// See [`Float::log_base_rational_float_base_prec_round`] for details, special cases, and a
279    /// description of the rounding behavior.
280    ///
281    /// # Worst-case complexity
282    /// $T(n) = O(n (\log n)^2 \log\log n)$
283    ///
284    /// $M(n) = O(n (\log n)^2)$
285    ///
286    /// where $T$ is time, $M$ is additional memory, and $n$ is `prec`.
287    ///
288    /// # Panics
289    /// Panics if `prec` is zero, or if `rm` is `Exact` but the result cannot be represented exactly
290    /// with the given precision.
291    ///
292    /// # Examples
293    /// ```
294    /// use malachite_base::rounding_modes::RoundingMode::*;
295    /// use malachite_float::Float;
296    /// use malachite_q::Rational;
297    /// use std::cmp::Ordering::*;
298    ///
299    /// let (log, o) = Float::log_base_rational_float_base_prec_round_ref(
300    ///     &Rational::from(9),
301    ///     &Float::from(3),
302    ///     10,
303    ///     Exact,
304    /// );
305    /// assert_eq!(log.to_string(), "2.0000"); // log_3(9) = 2
306    /// assert_eq!(o, Equal);
307    ///
308    /// let (log, o) = Float::log_base_rational_float_base_prec_round_ref(
309    ///     &Rational::from_signeds(1, 3),
310    ///     &Float::from(3),
311    ///     10,
312    ///     Exact,
313    /// );
314    /// assert_eq!(log.to_string(), "-1.0000"); // log_3(1/3) = -1
315    /// assert_eq!(o, Equal);
316    /// ```
317    pub fn log_base_rational_float_base_prec_round_ref(
318        x: &Rational,
319        base: &Self,
320        prec: u64,
321        rm: RoundingMode,
322    ) -> (Self, Ordering) {
323        assert_ne!(prec, 0);
324        log_base_rational_float_base_helper(x, base, prec, rm)
325    }
326
327    /// Computes $\log_b x$, where $x$ is a [`Rational`] and the base $b$ is a [`Float`], returning
328    /// a [`Float`] rounded to the nearest value of the specified precision. Both are taken by
329    /// value. An [`Ordering`] is also returned.
330    ///
331    /// See [`Float::log_base_rational_float_base_prec_round`] for details and special cases.
332    ///
333    /// # Worst-case complexity
334    /// $T(n) = O(n (\log n)^2 \log\log n)$
335    ///
336    /// $M(n) = O(n (\log n)^2)$
337    ///
338    /// where $T$ is time, $M$ is additional memory, and $n$ is `prec`.
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) = O(n (\log n)^2 \log\log n)$
372    ///
373    /// $M(n) = O(n (\log n)^2)$
374    ///
375    /// where $T$ is time, $M$ is additional memory, and $n$ is `prec`.
376    ///
377    /// # Panics
378    /// Panics if `prec` is zero.
379    ///
380    /// # Examples
381    /// ```
382    /// use malachite_float::Float;
383    /// use malachite_q::Rational;
384    /// use std::cmp::Ordering::*;
385    ///
386    /// let (log, o) =
387    ///     Float::log_base_rational_float_base_prec_ref(&Rational::from(9), &Float::from(3), 10);
388    /// assert_eq!(log.to_string(), "2.0000"); // log_3(9) = 2
389    /// assert_eq!(o, Equal);
390    /// ```
391    #[inline]
392    pub fn log_base_rational_float_base_prec_ref(
393        x: &Rational,
394        base: &Self,
395        prec: u64,
396    ) -> (Self, Ordering) {
397        Self::log_base_rational_float_base_prec_round_ref(x, base, prec, Nearest)
398    }
399}
400
401/// Computes $\log_b x$, the base-$b$ logarithm of a [`Rational`], where the base $b$ is a primitive
402/// float, returning a primitive float result. Using this function is more accurate than computing
403/// the logarithm using the standard library, whose logarithm functions are not always correctly
404/// rounded.
405///
406/// Unlike the integer- and rational-base logarithms, the base may be any primitive float: the
407/// function is defined as $\ln x / \ln b$ and never panics on an input value. A base in $(0,1)$
408/// gives a (sign-flipped) logarithm, and the non-normal and degenerate bases follow the limits
409/// below.
410///
411/// $$
412/// f(x,b) = \log_b x+\varepsilon.
413/// $$
414/// - If $\log_b x$ is infinite, zero, or `NaN`, $\varepsilon$ may be ignored or assumed to be 0.
415/// - If $\log_b x$ is finite and nonzero, then $|\varepsilon| < 2^{\lfloor\log_2 |\log_b
416///   x|\rfloor-p}$, where $p$ is precision of the output (typically 24 if `T` is a [`f32`] and 53
417///   if `T` is a [`f64`], but less if the output is subnormal).
418///
419/// Special cases (with $b$ the base):
420/// - $f(x,\text{NaN})=\text{NaN}$
421/// - $f(x,b)=\text{NaN}$ for $x<0$ or $b<0$ (including $b=-\infty$)
422/// - $f(0,b)=-\infty$ for $b>1$, and $\infty$ for $0<b<1$ (and $\text{NaN}$ for
423///   $b\in\{\infty,\pm0.0\}$)
424/// - $f(1,b)=0.0$ (with the sign of $1/\ln b$)
425/// - $f(x,\infty)=0.0$ for $x>0$ (and $\text{NaN}$ for $x=0$)
426/// - $f(x,\pm0.0)=0.0$ for $x>0$ (and $\text{NaN}$ for $x=0$)
427/// - $f(x,1.0)=\infty$ for $x>1$, $-\infty$ for $0\leq x<1$, and $\text{NaN}$ for $x=1$
428///
429/// This function can both overflow (for a base near 1) and underflow (for an $x$ near 1).
430///
431/// # Worst-case complexity
432/// Constant time and additional memory.
433///
434/// # Examples
435/// ```
436/// use malachite_base::num::float::NiceFloat;
437/// use malachite_float::float::arithmetic::log_base_rational_float_base::*;
438/// use malachite_q::Rational;
439///
440/// // log_4(8) = 3/2
441/// assert_eq!(
442///     NiceFloat(primitive_float_log_base_rational_float_base::<f32>(
443///         &Rational::from(8),
444///         4.0
445///     )),
446///     NiceFloat(1.5)
447/// );
448/// // log_(1/2)(4) = -2
449/// assert_eq!(
450///     NiceFloat(primitive_float_log_base_rational_float_base::<f32>(
451///         &Rational::from(4),
452///         0.5
453///     )),
454///     NiceFloat(-2.0)
455/// );
456/// // log_10(1/3)
457/// assert_eq!(
458///     NiceFloat(primitive_float_log_base_rational_float_base::<f32>(
459///         &Rational::from_unsigneds(1u8, 3),
460///         10.0
461///     )),
462///     NiceFloat(-0.47712126)
463/// );
464/// assert!(
465///     primitive_float_log_base_rational_float_base::<f32>(&Rational::from(-1), 10.0).is_nan()
466/// );
467/// assert!(
468///     primitive_float_log_base_rational_float_base::<f32>(&Rational::from(8), f32::NAN).is_nan()
469/// );
470/// ```
471#[inline]
472#[allow(clippy::type_repetition_in_bounds)]
473pub fn primitive_float_log_base_rational_float_base<T: PrimitiveFloat>(x: &Rational, base: T) -> T
474where
475    Float: From<T> + PartialOrd<T>,
476    for<'a> T: ExactFrom<&'a Float> + RoundingFrom<&'a Float>,
477{
478    emulate_float_to_float_fn(
479        |base2, prec| Float::log_base_rational_float_base_prec_ref(x, &base2, prec),
480        base,
481    )
482}