Skip to main content

malachite_float/float/constants/
liouvilles_constant.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::{Float, emulate_constant_to_float_fn};
10use core::cmp::Ordering;
11use malachite_base::num::arithmetic::traits::SaturatingMulAssign;
12use malachite_base::num::basic::floats::PrimitiveFloat;
13use malachite_base::num::conversion::traits::{ExactFrom, RoundingFrom};
14use malachite_base::rounding_modes::RoundingMode::{self, Nearest};
15
16// The digits of Liouville's constant in the given base: 1 at every position that is a factorial, 0
17// everywhere else. The positions are 1-indexed, so both 1! and 2! contribute a 1, making the
18// constant begin 0.11000100....
19//
20// The multiplication saturates rather than overflowing, which cannot be observed: 21! already
21// exceeds a `u64`, and reaching that position would mean reading more digits than any precision
22// could ask for.
23fn liouvilles_digits() -> impl Iterator<Item = u64> {
24    let mut position = 0u64;
25    let mut factorial = 1u64;
26    let mut index = 1u64;
27    core::iter::from_fn(move || {
28        position += 1;
29        Some(if position == factorial {
30            index += 1;
31            factorial.saturating_mul_assign(index);
32            1
33        } else {
34            0
35        })
36    })
37}
38
39impl Float {
40    /// Returns an approximation of Liouville's constant in a given base, with the given precision
41    /// and rounded using the given [`RoundingMode`]. An [`Ordering`] is also returned, indicating
42    /// whether the rounded value is less than or greater than the exact value of the constant.
43    /// (Since the constant is irrational, the rounded value is never equal to the exact value.)
44    ///
45    /// Liouville's constant in base $b$ has a digit of 1 at every position that is a factorial and
46    /// a digit of 0 everywhere else. That is,
47    /// $$
48    /// L_b = \sum_{n=1}^\infty b^{-n!}+\varepsilon.
49    /// $$
50    /// - If $m$ is not `Nearest`, then $|\varepsilon| < 2^{\lfloor\log_2 L_b\rfloor-p+1}$.
51    /// - If $m$ is `Nearest`, then $|\varepsilon| < 2^{\lfloor\log_2 L_b\rfloor-p}$.
52    ///
53    /// Base 10 gives the classical constant, $0.110001000000000000000001\ldots$, the first number
54    /// proven transcendental. The constant is transcendental in every base, being a Liouville
55    /// number: its factorial-spaced digits make it approximable by rationals far too well for an
56    /// algebraic number.
57    ///
58    /// The output has precision `prec`.
59    ///
60    /// # Worst-case complexity
61    /// $T(n) = O(n (\log n)^2 \log\log n)$
62    ///
63    /// $M(n) = O(n \log n)$
64    ///
65    /// where $T$ is time, $M$ is additional memory, and $n$ is `prec`.
66    ///
67    /// # Panics
68    /// Panics if `base` is less than 2, if `prec` is zero, or if `rm` is `Exact`.
69    ///
70    /// # Examples
71    /// ```
72    /// use malachite_base::rounding_modes::RoundingMode::*;
73    /// use malachite_float::Float;
74    /// use std::cmp::Ordering::*;
75    ///
76    /// // In base 2 the constant has a 1 at every factorial position -- 1, 2, 6, 24, ... -- and a
77    /// // 0 everywhere else, which the binary representation shows directly.
78    /// let (x, o) = Float::liouvilles_constant_base_prec_round(2, 64, Floor);
79    /// assert_eq!(x.to_string(), "0.765625059604644775391");
80    /// assert_eq!(
81    ///     format!("{x:#b}"),
82    ///     "0b0.1100010000000000000000010000000000000000000000000000000000000000"
83    /// );
84    /// assert_eq!(o, Less);
85    ///
86    /// let (x, o) = Float::liouvilles_constant_base_prec_round(2, 64, Ceiling);
87    /// assert_eq!(x.to_string(), "0.765625059604644775445");
88    /// assert_eq!(
89    ///     format!("{x:#b}"),
90    ///     "0b0.1100010000000000000000010000000000000000000000000000000000000001"
91    /// );
92    /// assert_eq!(o, Greater);
93    /// ```
94    #[inline]
95    pub fn liouvilles_constant_base_prec_round(
96        base: u64,
97        prec: u64,
98        rm: RoundingMode,
99    ) -> (Self, Ordering) {
100        Self::non_dyadic_from_digits_prec_round(liouvilles_digits(), base, prec, rm)
101    }
102
103    /// Returns an approximation of Liouville's constant in a given base, with the given precision
104    /// and rounded to the nearest [`Float`] of that precision. An [`Ordering`] is also returned,
105    /// indicating whether the rounded value is less than or greater than the exact value.
106    ///
107    /// See [`liouvilles_constant_base_prec_round`](Float::liouvilles_constant_base_prec_round) for
108    /// details.
109    ///
110    /// # Worst-case complexity
111    /// $T(n) = O(n (\log n)^2 \log\log n)$
112    ///
113    /// $M(n) = O(n \log n)$
114    ///
115    /// where $T$ is time, $M$ is additional memory, and $n$ is `prec`.
116    ///
117    /// # Panics
118    /// Panics if `base` is less than 2 or if `prec` is zero.
119    ///
120    /// # Examples
121    /// ```
122    /// use malachite_float::Float;
123    /// use std::cmp::Ordering::*;
124    ///
125    /// // In base 2 the 1s sit at the factorial positions 1, 2, 6, 24, ...
126    /// let (x, o) = Float::liouvilles_constant_base_prec(2, 64);
127    /// assert_eq!(x.to_string(), "0.765625059604644775391");
128    /// assert_eq!(
129    ///     format!("{x:#b}"),
130    ///     "0b0.1100010000000000000000010000000000000000000000000000000000000000"
131    /// );
132    /// assert_eq!(o, Less);
133    ///
134    /// // A base that is not a power of 2 takes the general path, where the digits cannot simply
135    /// // be read off.
136    /// let (x, o) = Float::liouvilles_constant_base_prec(3, 50);
137    /// assert_eq!(x.to_string(), "0.44581618656046818");
138    /// assert_eq!(o, Greater);
139    /// ```
140    #[inline]
141    pub fn liouvilles_constant_base_prec(base: u64, prec: u64) -> (Self, Ordering) {
142        Self::liouvilles_constant_base_prec_round(base, prec, Nearest)
143    }
144
145    /// Returns an approximation of Liouville's constant in base 10, with the given precision and
146    /// rounded using the given [`RoundingMode`]. An [`Ordering`] is also returned, indicating
147    /// whether the rounded value is less than or greater than the exact value of the constant.
148    /// (Since the constant is irrational, the rounded value is never equal to the exact value.)
149    ///
150    /// Liouville's constant has a decimal digit of 1 at every position that is a factorial and a
151    /// digit of 0 everywhere else, so it begins $0.110001000000000000000001\ldots$.
152    ///
153    /// $$
154    /// x = L = \sum_{n=1}^{\infty} 10^{-n!}+\varepsilon.
155    /// $$
156    /// - If $m$ is not `Nearest`, then $|\varepsilon| < 2^{-p}$.
157    /// - If $m$ is `Nearest`, then $|\varepsilon| < 2^{-p-1}$.
158    ///
159    /// The constant is irrational and transcendental.
160    ///
161    /// The output has precision `prec`.
162    ///
163    /// This is the base-10 specialization of
164    /// [`liouvilles_constant_base_prec_round`](Float::liouvilles_constant_base_prec_round).
165    ///
166    /// # Worst-case complexity
167    /// $T(n) = O(n \log n \log\log n)$
168    ///
169    /// $M(n) = O(n \log n)$
170    ///
171    /// where $T$ is time, $M$ is additional memory, and $n$ is `prec`.
172    ///
173    /// # Panics
174    /// Panics if `prec` is zero or if `rm` is `Exact`.
175    ///
176    /// # Examples
177    /// ```
178    /// use malachite_base::rounding_modes::RoundingMode::*;
179    /// use malachite_float::Float;
180    /// use std::cmp::Ordering::*;
181    ///
182    /// let (x, o) = Float::liouvilles_constant_prec_round(100, Floor);
183    /// assert_eq!(x.to_string(), "0.11000100000000000000000099999997");
184    /// assert_eq!(o, Less);
185    ///
186    /// let (x, o) = Float::liouvilles_constant_prec_round(100, Ceiling);
187    /// assert_eq!(x.to_string(), "0.11000100000000000000000100000007");
188    /// assert_eq!(o, Greater);
189    /// ```
190    #[inline]
191    pub fn liouvilles_constant_prec_round(prec: u64, rm: RoundingMode) -> (Self, Ordering) {
192        Self::liouvilles_constant_base_prec_round(10, prec, rm)
193    }
194
195    /// Returns an approximation of Liouville's constant in base 10, with the given precision and
196    /// rounded to the nearest [`Float`] of that precision. An [`Ordering`] is also returned,
197    /// indicating whether the rounded value is less than or greater than the exact value of the
198    /// constant. (Since the constant is irrational, the rounded value is never equal to the exact
199    /// value.)
200    ///
201    /// Liouville's constant has a decimal digit of 1 at every position that is a factorial and a
202    /// digit of 0 everywhere else, so it begins $0.110001000000000000000001\ldots$.
203    ///
204    /// $$
205    /// x = L = \sum_{n=1}^{\infty} 10^{-n!}+\varepsilon.
206    /// $$
207    /// - $|\varepsilon| < 2^{-p-1}$.
208    ///
209    /// The constant is irrational and transcendental.
210    ///
211    /// The output has precision `prec`.
212    ///
213    /// This is the base-10 specialization of
214    /// [`liouvilles_constant_base_prec`](Float::liouvilles_constant_base_prec).
215    ///
216    /// # Worst-case complexity
217    /// $T(n) = O(n \log n \log\log n)$
218    ///
219    /// $M(n) = O(n \log n)$
220    ///
221    /// where $T$ is time, $M$ is additional memory, and $n$ is `prec`.
222    ///
223    /// # Panics
224    /// Panics if `prec` is zero.
225    ///
226    /// # Examples
227    /// ```
228    /// use malachite_float::Float;
229    ///
230    /// let x = Float::liouvilles_constant_prec(100).0;
231    /// assert_eq!(x.to_string(), "0.11000100000000000000000099999997");
232    /// ```
233    #[inline]
234    pub fn liouvilles_constant_prec(prec: u64) -> (Self, Ordering) {
235        Self::liouvilles_constant_base_prec(10, prec)
236    }
237}
238
239/// Computes an approximation of Liouville's constant in a given base, returning a primitive float.
240///
241/// Liouville's constant in base $b$ has a digit of 1 at every position that is a factorial and a
242/// digit of 0 everywhere else.
243///
244/// $$
245/// L_b = \sum_{n=1}^{\infty} b^{-n!}.
246/// $$
247///
248/// The returned value is the one closest to the true constant; ties are broken by the
249/// round-half-to-even rule. Computing the constant this way is more accurate than summing its
250/// digits in primitive-float arithmetic, where each addition rounds.
251///
252/// $$
253/// f(b) = L_b+\varepsilon,
254/// $$
255/// where $|\varepsilon| < 2^{\lfloor\log_2 |L_b|\rfloor-p}$ and $p$ is the precision of the output
256/// (24 if `T` is a [`f32`] and 53 if `T` is a [`f64`]).
257///
258/// The constant lies in $[1/b,1)$, and $b$ is at most $2^{64}-1$, so this function can neither
259/// overflow nor underflow.
260///
261/// # Worst-case complexity
262/// Constant time and additional memory.
263///
264/// # Panics
265/// Panics if `base` is less than 2.
266///
267/// # Examples
268/// ```
269/// use malachite_base::num::float::NiceFloat;
270/// use malachite_float::float::constants::liouvilles_constant::*;
271///
272/// // The classical constant, 0.110001000000000000000001...
273/// assert_eq!(
274///     NiceFloat(primitive_float_liouvilles_constant_base::<f32>(10)),
275///     NiceFloat(0.110001)
276/// );
277/// assert_eq!(
278///     NiceFloat(primitive_float_liouvilles_constant_base::<f64>(10)),
279///     NiceFloat(0.110001)
280/// );
281/// // In base 2 the constant is 0.11000100000000000000000100...
282/// assert_eq!(
283///     NiceFloat(primitive_float_liouvilles_constant_base::<f64>(2)),
284///     NiceFloat(0.7656250596046448)
285/// );
286/// ```
287#[inline]
288#[allow(clippy::type_repetition_in_bounds)]
289pub fn primitive_float_liouvilles_constant_base<T: PrimitiveFloat>(base: u64) -> T
290where
291    Float: PartialOrd<T>,
292    for<'a> T: ExactFrom<&'a Float> + RoundingFrom<&'a Float>,
293{
294    emulate_constant_to_float_fn(|prec| Float::liouvilles_constant_base_prec(base, prec))
295}