Skip to main content

malachite_float/float/conversion/string/
set_str.rs

1// Copyright © 2026 Mikhail Hogrefe
2//
3// Uses code adopted from the GNU MPFR Library.
4//
5//      Copyright © 2004-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;
17use core::cmp::Ordering::{self, *};
18use malachite_base::num::arithmetic::traits::{IsPowerOf2, Sign};
19use malachite_base::num::basic::traits::{Infinity, NegativeInfinity, NegativeZero, Zero};
20use malachite_base::num::conversion::traits::ExactFrom;
21use malachite_base::rounding_modes::RoundingMode::{self, *};
22use malachite_nz::natural::Natural;
23use malachite_nz::natural::arithmetic::float_extras::{SetStrResult, limbs_set_str};
24
25// The value is larger in magnitude than any finite `Float` of precision `prec`.
26//
27// This is `mpfr_overflow` from `exceptions.c`, MPFR 4.3.0.
28pub(crate) fn overflow(sign: bool, prec: u64, rm: RoundingMode) -> (Float, Ordering) {
29    match (sign, rm) {
30        (true, Up | Ceiling | Nearest) => (Float::INFINITY, Greater),
31        (true, Floor | Down) => (Float::max_finite_value_with_prec(prec), Less),
32        (false, Up | Floor | Nearest) => (Float::NEGATIVE_INFINITY, Less),
33        (false, Ceiling | Down) => (-Float::max_finite_value_with_prec(prec), Greater),
34        (_, Exact) => panic!("Inexact conversion from string to Float"),
35    }
36}
37
38// The value is smaller in magnitude than the least positive `Float` of precision `prec`. Under
39// `Nearest` the result is the least positive value; the caller substitutes `Down` in the cases
40// where `mpfr_check_range` does.
41//
42// This is `mpfr_underflow` from `exceptions.c`, MPFR 4.3.0.
43fn underflow(sign: bool, prec: u64, rm: RoundingMode) -> (Float, Ordering) {
44    match (sign, rm) {
45        (true, Up | Ceiling | Nearest) => (Float::min_positive_value_prec(prec), Greater),
46        (true, Floor | Down) => (Float::ZERO, Less),
47        (false, Up | Floor | Nearest) => (-Float::min_positive_value_prec(prec), Less),
48        (false, Ceiling | Down) => (Float::NEGATIVE_ZERO, Greater),
49        (_, Exact) => panic!("Inexact conversion from string to Float"),
50    }
51}
52
53pub_crate_test! {
54// Converts a parsed digit string to a `Float` of precision `prec`, correctly rounded with `rm`.
55//
56// `digits` holds digit values (not characters), most significant first, with leading and trailing
57// zeros already stripped; it must be nonempty, so a zero value is the caller's business. `exp_base`
58// is the number of digits before the point plus any base-`base` exponent, and `exp_bin` an extra
59// binary exponent (the `p` form of the input), zero when there is none.
60//
61// This is the tail of `parsed_string_to_mpfr` from `strtofr.c`, MPFR 4.3.0, together with the
62// `mpfr_check_range` call that follows it.
63set_str_helper(
64    sign: bool,
65    digits: &[u8],
66    base: u8,
67    exp_base: i64,
68    exp_bin: i64,
69    prec: u64,
70    rm: RoundingMode,
71) -> (Float, Ordering) {
72    assert_ne!(prec, 0);
73    // `limbs_set_str` rounds the magnitude, so it takes the rounding mode inverted for a negative
74    // value, and the direction it returns refers to the magnitude too.
75    let mag_rm = if sign { rm } else { -rm };
76    let away_from_0 = if sign { Greater } else { Less };
77    match limbs_set_str(digits, u64::from(base), exp_base, exp_bin, prec, mag_rm) {
78        SetStrResult::Overflow => overflow(sign, prec, rm),
79        SetStrResult::Underflow => underflow(sign, prec, if rm == Nearest { Down } else { rm }),
80        SetStrResult::Finite(significand, exp, dir) => {
81            // `round_helper_raw` rounds under `Exact` as if it were `Down` rather than complaining,
82            // so the contract is enforced here, as it is at the other call sites.
83            assert!(
84                rm != Exact || dir == 0,
85                "Inexact conversion from string to Float"
86            );
87            let o = if sign { dir.sign() } else { dir.sign().reverse() };
88            let significand = Natural::from_owned_limbs_asc(significand);
89            // mpfr_check_range
90            if exp > i64::from(Float::MAX_EXPONENT) {
91                return overflow(sign, prec, rm);
92            }
93            if exp < i64::from(Float::MIN_EXPONENT) {
94                // Under `Nearest` the result rounds away from zero, to the least positive value,
95                // only when it is at least half of it: the exponent must be exactly one below the
96                // minimum, and at exactly half (a power of two) the discarded part must have been
97                // nonzero.
98                let rm = if rm == Nearest
99                    && !(exp == i64::from(Float::MIN_EXPONENT_MINUS_1)
100                        && (o == away_from_0.reverse() || !significand.is_power_of_2()))
101                {
102                    Down
103                } else {
104                    rm
105                };
106                return underflow(sign, prec, rm);
107            }
108            (
109                Float(Finite {
110                    sign,
111                    exponent: i32::exact_from(exp),
112                    precision: prec,
113                    significand,
114                }),
115                o,
116            )
117        }
118    }
119}}