Skip to main content

malachite_float/float/conversion/string/
get_str.rs

1// Copyright © 2026 Mikhail Hogrefe
2//
3// Uses code adopted from the GNU MPFR Library.
4//
5//      Copyright © 1999-2024 Free Software Foundation, Inc.
6//
7//      Contributed by the AriC and Caramba projects, INRIA.
8//
9// This file is part of Malachite.
10//
11// Malachite is free software: you can redistribute it and/or modify it under the terms of the GNU
12// Lesser General Public License (LGPL) as published by the Free Software Foundation; either version
13// 3 of the License, or (at your option) any later version. See <https://www.gnu.org/licenses/>.
14
15use crate::Float;
16use crate::InnerFloat::{Finite, Infinity, NaN, Zero};
17use crate::float::conversion::string::get_str_data::MPFR_L2B;
18use crate::floor_and_ceiling;
19use alloc::vec;
20use alloc::vec::Vec;
21use core::cmp::Ordering::{self, Equal};
22use malachite_base::fail_on_untested_path;
23use malachite_base::num::arithmetic::traits::{CeilingLogBase2, CheckedLogBase2, NegAssign, Sign};
24use malachite_base::num::basic::integers::PrimitiveInt;
25use malachite_base::num::conversion::traits::{ExactFrom, RoundingFrom};
26use malachite_base::rounding_modes::RoundingMode::{self, Ceiling, Exact, Floor};
27use malachite_nz::natural::Natural;
28use malachite_nz::natural::arithmetic::float::get_str::{limbs_get_str, limbs_get_str_power_of_2};
29
30// Returns `ceil(e * log2(beta) ^ ((-1) ^ i))`, or that plus 1. For `i == 0` it uses a 23-bit upper
31// approximation to `log(beta) / log(2)`; for `i == 1` a 77-bit upper approximation to `log(2) /
32// log(beta)`. Both approximations are entries of `MPFR_L2B`.
33//
34// This is `mpfr_ceil_mul` from `get_str.c`, MPFR 4.2.2.
35crate_test_fn! {ceil_mul(e: i64, beta: u64, i: usize) -> i64 {
36    const WIDTH_MINUS_1: u64 = i64::WIDTH - 1;
37    // p = mantissa * 2 ^ (exp - 128): the l2b approximation as an exact `Float`.
38    let (mantissa, exp) = MPFR_L2B[usize::exact_from(beta) - 2][i];
39    let p = Float::from_natural_prec(Natural::from(mantissa), 128).0
40        >> u64::exact_from(128 - i64::from(exp));
41    // t = e * p, with e as a `Float` with the precision of an `mpfr_exp_t` minus one, both
42    // roundings up.
43    let t = Float::from_signed_prec_round(e, WIDTH_MINUS_1, Ceiling)
44        .0
45        .mul_prec_round(p, WIDTH_MINUS_1, Ceiling)
46        .0;
47    // ceil(t).
48    i64::rounding_from(&t, Ceiling).0
49}}
50
51/// Returns the number of significant digits that suffice to losslessly represent any [`Float`] of
52/// precision `prec` in base `base`: printing such a [`Float`] to this many digits with rounding to
53/// nearest (for example with [`get_str`]), then reading the digits back at precision `prec`, again
54/// rounding to nearest, recovers the original value exactly.
55///
56/// The count is $1 + \lceil p \log 2 / \log b \rceil$, except that for a power-of-2 base $b = 2^k$
57/// it is $1 + \lceil (p - 1) / k \rceil$.
58///
59/// This function depends only on the precision, not on any particular [`Float`] value. It is the
60/// digit count [`get_str`] uses when its `digit_len` argument is 0, and the number of significant
61/// digits `Display` shows for a [`Float`] of precision `prec`.
62///
63/// # Worst-case complexity
64/// Constant time and additional memory.
65///
66/// # Panics
67/// Panics if `base` is less than 2 or greater than 62, or if `prec` is 0.
68///
69/// # Examples
70/// ```
71/// use malachite_float::float::conversion::string::get_str::get_str_digit_count;
72///
73/// // 17 significant decimal digits distinguish every double-precision (53-bit) value...
74/// assert_eq!(get_str_digit_count(10, 53), 17);
75/// // ...and 9 suffice for single precision (24 bits)
76/// assert_eq!(get_str_digit_count(10, 24), 9);
77/// // in base 16, 14 digits: 1 + ceil(52 / 4)
78/// assert_eq!(get_str_digit_count(16, 53), 14);
79/// // in base 2 the digits are just the bits
80/// assert_eq!(get_str_digit_count(2, 53), 53);
81/// ```
82///
83/// This is `mpfr_get_str_ndigits` from `get_str.c`, MPFR 4.2.2.
84pub fn get_str_digit_count(base: u64, prec: u64) -> usize {
85    assert!((2..=62).contains(&base));
86    assert_ne!(prec, 0);
87    // Deal first with power-of-two bases, since even for those, `ceil_mul` might return a value too
88    // large by 1. For `base = 2 ^ k`, this is `1 + ceil((prec - 1) / k) = 2 + floor((prec - 2) /
89    // k)`.
90    if let Some(k) = base.checked_log_base_2() {
91        return usize::exact_from(1 + (prec + k - 2) / k);
92    }
93    // `ceil_mul` is guaranteed to give `1 + ceil(prec * log(2) / log(base))` for `prec` below this
94    // bound (for `prec = 186564318007` and `base = 7` or `49` it returns one more).
95    let ret = if prec < 186_564_318_007 {
96        u64::exact_from(ceil_mul(i64::exact_from(prec), base, 1))
97    } else {
98        // `prec` is large and `base` is not a power of two, so `prec * log(2) / log(base)` cannot
99        // be an integer and Ziv's loop terminates. `w` is the working precision; `ceil_mul` used a
100        // 77-bit upper approximation to `log(2) / log(base)`. Reaching here needs a mantissa of at
101        // least ~1.86e11 bits, far beyond any `Float` the test suite builds.
102        fail_on_untested_path("get_str_digit_count, Ziv loop for huge prec");
103        let mut w = 77;
104        loop {
105            w <<= 1;
106            // lower (rounding down) and upper (rounding up) approximations to `log2(base)`
107            let (log_lo, log_hi) =
108                floor_and_ceiling(Float::from_unsigned_prec(base, w).0.log_base_2_round(Floor));
109            // lower (`prec / log_hi`, rounding down) and upper (`prec / log_lo`, rounding up)
110            // bounds on `prec * log(2) / log(base)`, each rounded up to an integer
111            let pf = Float::from_unsigned_prec(prec, w).0;
112            let lo = u64::rounding_from(&pf.div_round_ref_val(log_hi, Floor).0, Ceiling).0;
113            let hi = u64::rounding_from(&pf.div_round(log_lo, Ceiling).0, Ceiling).0;
114            if lo == hi {
115                break lo;
116            }
117        }
118    };
119    usize::exact_from(1 + ret)
120}
121
122/// Converts a [`Float`] to base-`base` mantissa digits and an exponent, rounding to `digit_len`
123/// digits with the rounding mode `rm`.
124///
125/// The digits are returned as ASCII characters (`0`–`9`, then lowercase `a`–`z`, then uppercase
126/// `A`–`Z`, supporting a `base` of up to 62; a negative `base` in `-36..=-2` uses base `|base|`
127/// with `0`–`9` and uppercase `A`–`Z`), preceded by `-` when `x` is negative. With the returned
128/// exponent $e$, the value represented is $0.d_1 d_2 \ldots \times \mathrm{base}^e$, where $d_1 d_2
129/// \ldots$ are the digits. If `digit_len` is 0, the fewest digits that round-trip back to `x` are
130/// used.
131///
132/// `base` must be in `2..=62` or `-36..=-2`; any other value returns `None`.
133///
134/// The returned [`Ordering`] reports whether the rounded result is less than, equal to, or greater
135/// than the exact value of `x`. The special values NaN, $\infty$, and $-\infty$ produce the strings
136/// `@NaN@`, `@Inf@`, and `-@Inf@`, each with exponent 0 and `Equal`.
137///
138/// # Worst-case complexity
139/// $T(n) = O(n (\log n)^2 \log\log n)$
140///
141/// $M(n) = O(n \log n)$
142///
143/// where $T$ is time, $M$ is additional memory, and $n$ is `max(x.complexity(), digit_len)`.
144///
145/// # Panics
146/// Panics if `rm` is `Exact` but `x` cannot be represented exactly in `digit_len` base-`base`
147/// digits.
148///
149/// # Examples
150/// ```
151/// use core::cmp::Ordering::*;
152/// use malachite_base::rounding_modes::RoundingMode::{self, *};
153/// use malachite_float::float::conversion::string::get_str::get_str;
154/// use malachite_float::Float;
155/// use malachite_q::Rational;
156///
157/// // Render the returned digit bytes as a `String` for readability.
158/// let s = |x: &Float, base: i64, n: usize, rm: RoundingMode| {
159///     get_str(x, base, n, rm)
160///         .map(|(digits, exp, ord)| (String::from_utf8(digits).unwrap(), exp, ord))
161/// };
162///
163/// // 1.25 to 3 digits: 0.125 * 10^1 in base 10, 0.101 * 2^1 in base 2, both exact.
164/// assert_eq!(
165///     s(&Float::from(1.25), 10, 3, Nearest),
166///     Some(("125".to_string(), 1, Equal))
167/// );
168/// assert_eq!(
169///     s(&Float::from(1.25), 2, 3, Nearest),
170///     Some(("101".to_string(), 1, Equal))
171/// );
172///
173/// // A negative value gets a leading `-`.
174/// assert_eq!(
175///     s(&Float::from(-1.25), 10, 3, Nearest),
176///     Some(("-125".to_string(), 1, Equal))
177/// );
178///
179/// // 1/3 (to 53 bits) has no finite base-10 expansion, so the result is rounded and the
180/// // `Ordering` gives the direction.
181/// let third = Float::from_rational_prec(Rational::from_unsigneds(1u32, 3u32), 53).0;
182/// assert_eq!(s(&third, 10, 4, Floor), Some(("3333".to_string(), 0, Less)));
183/// assert_eq!(
184///     s(&third, 10, 4, Ceiling),
185///     Some(("3334".to_string(), 0, Greater))
186/// );
187///
188/// // Special values produce fixed strings; an invalid base gives `None`.
189/// assert_eq!(
190///     s(&Float::from(f64::NAN), 2, 0, Down),
191///     Some(("@NaN@".to_string(), 0, Equal))
192/// );
193/// assert_eq!(
194///     s(&Float::from(f64::INFINITY), 2, 0, Down),
195///     Some(("@Inf@".to_string(), 0, Equal))
196/// );
197/// assert_eq!(s(&Float::from(1.25), 100, 0, Nearest), None);
198/// ```
199///
200/// This is mpfr_get_str from get_str.c, MPFR 4.2.2.
201pub fn get_str(
202    x: &Float,
203    base: i64,
204    digit_len: usize,
205    mut rm: RoundingMode,
206) -> Option<(Vec<u8>, i64, Ordering)> {
207    // valid bases are -36..=-2 and 2..=62
208    if !(-36..=-2).contains(&base) && !(2..=62).contains(&base) {
209        return None;
210    }
211    let b = base.unsigned_abs();
212    // `dir` is the direction in which the magnitude of `x` was rounded to the result (-1, 0, or 1).
213    let (neg, mut s, e, dir) = match &x.0 {
214        NaN => return Some((b"@NaN@".to_vec(), 0, Equal)),
215        Infinity { sign } => {
216            let s = if *sign {
217                b"@Inf@".to_vec()
218            } else {
219                b"-@Inf@".to_vec()
220            };
221            return Some((s, 0, Equal));
222        }
223        Zero { sign } => {
224            // Malachite's zero carries no precision, so the digit_len == 0 default
225            // (get_str_digit_count) does not apply; use a single digit.
226            (
227                !*sign,
228                vec![b'0'; if digit_len == 0 { 1 } else { digit_len }],
229                0,
230                0,
231            )
232        }
233        Finite {
234            sign,
235            exponent,
236            precision,
237            significand,
238        } => {
239            let m = if digit_len == 0 {
240                get_str_digit_count(b, *precision)
241            } else {
242                digit_len
243            };
244            // For a negative x, reduce to the magnitude by inverting the rounding direction (the
245            // mpfr_get_str MPFR_INVERT_RND step).
246            let neg = !*sign;
247            if neg {
248                rm.neg_assign();
249            }
250            // Malachite's `exponent` is MPFR's EXP (the scientific exponent plus one).
251            let xp = significand.to_limbs_asc();
252            let x_exp = i64::from(*exponent);
253            let (s, e, dir) = if b.is_power_of_two() {
254                limbs_get_str_power_of_2(&xp, x_exp, *precision, b, base, m, rm)
255            } else {
256                let g = ceil_mul(x_exp - 1, b, 1);
257                let exp = (i64::exact_from(m) - g).unsigned_abs();
258                // radix-2 precision needed for m digits in base b, plus guard bits
259                let mut prec = u64::exact_from(ceil_mul(i64::exact_from(m), b, 0)) + 1;
260                prec += prec.ceiling_log_base_2();
261                if exp != 0 {
262                    // add the maximal exponentiation error
263                    prec += 3 * exp.ceiling_log_base_2();
264                }
265                limbs_get_str(&xp, x_exp, b, base, m, rm, g, prec, i64::exact_from(exp))
266            };
267            (neg, s, e, dir)
268        }
269    };
270    // `Exact` demands that the digits represent `x` exactly; a nonzero `dir` means rounding was
271    // needed, which violates the contract. (For odd bases this is common, since a dyadic `Float`
272    // rarely has a finite expansion there; and `digit_len == 0` picks the round-trip digit count,
273    // which is generally fewer than the exact expansion needs.)
274    assert!(
275        rm != Exact || dir == 0,
276        "get_str: Exact rounding was requested, but {x} is not exactly representable in the \
277         requested number of base-{base} digits"
278    );
279    // `dir` orders the result's magnitude against `|x|`; negating both reverses the order.
280    let o = dir.sign();
281    Some(if neg {
282        s.insert(0, b'-');
283        (s, e, o.reverse())
284    } else {
285        (s, e, o)
286    })
287}