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}}