Skip to main content

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