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}