malachite_float/float/conversion/string/from_sci_string.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//
9// Malachite's own scientific-string parsing, driven by `FromSciStringOptions`: the counterpart of
10// `to_sci.rs`, and the reverse-direction sibling of `strtofr.rs`.
11//
12// The grammar is the one the rest of Malachite uses, `preprocess_sci_string` from malachite-base,
13// so a `Float` reads a string the same way a `Rational` does. That differs from MPFR's, which
14// `strtofr.rs` implements instead; the two never disagree about a string's value, but each accepts
15// some the other rejects (see PORTING.md). What `Float` adds over `Rational` is the three special
16// values, a signed zero, and a precision and rounding mode, with the ternary value they imply.
17//
18// The digits are handed to the same `set_str_helper` core that `strtofr.rs` uses, so the arithmetic
19// is shared and only the grammar differs.
20
21use crate::Float;
22use crate::float::conversion::string::get_str::ceil_mul;
23use crate::float::conversion::string::set_str::{overflow, set_str_helper};
24use alloc::borrow::Cow;
25use alloc::format;
26use alloc::vec::Vec;
27use core::cmp::Ordering::{self, *};
28use core::str::FromStr;
29use malachite_base::num::basic::traits::{Infinity, NaN, NegativeInfinity, NegativeZero, Zero};
30use malachite_base::num::conversion::string::from_sci_string::preprocess_sci_string;
31use malachite_base::num::conversion::string::from_string::digit_from_display_byte;
32use malachite_base::num::conversion::string::options::FromSciStringOptions;
33use malachite_base::num::conversion::traits::{
34 ExactFrom, FromSciString, IntegerMantissaAndExponent,
35};
36use malachite_base::num::logic::traits::SignificantBits;
37use malachite_base::rounding_modes::RoundingMode::{self, *};
38
39// The outcome of reading a string's digits, before any rounding.
40enum Parsed {
41 // A NaN or an infinity, which carry no precision.
42 Special(Float),
43 // A zero with the given sign.
44 Zero(bool),
45 // The sign, the digit values (most significant first, with leading and trailing zeros
46 // stripped), the number of digits before the point plus any exponent, and how many significant
47 // digits the string had before the trailing zeros were stripped.
48 Finite(bool, Vec<u8>, i64, usize),
49 // The exponent is too large for the value to be finite.
50 Overflow(bool),
51}
52
53// Reads `s` in the base given by `options`, returning `None` if it is not a valid number.
54fn parse(s: &str, options: FromSciStringOptions) -> Option<Parsed> {
55 // The special values, spelled as `Float`'s `Display` writes them.
56 match s {
57 "NaN" => return Some(Parsed::Special(Float::NAN)),
58 "Infinity" => return Some(Parsed::Special(Float::INFINITY)),
59 "-Infinity" => return Some(Parsed::Special(Float::NEGATIVE_INFINITY)),
60 _ => {}
61 }
62 let base = options.get_base();
63 // `preprocess_sci_string` removes the point and folds it, and any exponent, into a power of the
64 // base: the result is the digit characters and an exponent `e` with the value being those
65 // digits, read as an integer, times base ^ e.
66 let (chars, exponent) = preprocess_sci_string(s, options)?;
67 let (sign, chars) = match chars.split_first() {
68 Some((&b'-', rest)) => (false, rest),
69 Some((&b'+', rest)) => (true, rest),
70 _ => (true, &chars[..]),
71 };
72 if chars.is_empty() {
73 return None;
74 }
75 let mut digits = Vec::with_capacity(chars.len());
76 for &c in chars {
77 let digit = digit_from_display_byte(c)?;
78 if digit >= base {
79 return None;
80 }
81 digits.push(digit);
82 }
83 // Restate the value as 0.d1 d2 ... times base ^ exp_base, the form the core takes.
84 let Some(mut exp_base) = i64::exact_from(digits.len()).checked_add(exponent) else {
85 // Only an exponent near the top of its range can do this, and the digit count is positive,
86 // so the sum can only have overflowed upwards.
87 return Some(Parsed::Overflow(sign));
88 };
89 // Leading zeros are not significant and each one lowers the exponent.
90 let leading = digits.iter().take_while(|&&d| d == 0).count();
91 digits.drain(..leading);
92 exp_base -= i64::exact_from(leading);
93 // Every digit that is left counts towards the precision the string implies, including the
94 // trailing zeros the core does not want.
95 let significant = digits.len();
96 while digits.last() == Some(&0) {
97 digits.pop();
98 }
99 Some(if digits.is_empty() {
100 Parsed::Zero(sign)
101 } else {
102 Parsed::Finite(sign, digits, exp_base, significant)
103 })
104}
105
106// The precision a string implies when the caller does not give one: as many bits as its significant
107// digits can carry.
108fn implied_prec(significant: usize, base: u8) -> u64 {
109 u64::exact_from(ceil_mul(i64::exact_from(significant), u64::from(base), 0))
110}
111
112impl Float {
113 /// Converts a string, possibly in scientific notation, to a [`Float`], with a given precision
114 /// and rounding mode.
115 ///
116 /// The string is read in base 10; use
117 /// [`from_sci_string_with_options_prec`](Float::from_sci_string_with_options_prec) for another
118 /// base. The result is the string's exact value rounded once to `prec` bits with `rm`, together
119 /// with the [`Ordering`] of the result against that exact value. `None` means the string is not
120 /// a number.
121 ///
122 /// See [`from_sci_string_with_options_prec`](Float::from_sci_string_with_options_prec) for the
123 /// grammar and for the treatment of the special values and of zero.
124 ///
125 /// # Worst-case complexity
126 /// $T(n) = O(n (\log n)^2 \log\log n)$
127 ///
128 /// $M(n) = O(n \log n)$
129 ///
130 /// where $T$ is time, $M$ is additional memory, and $n$ is `max(s.len(), prec)`.
131 ///
132 /// # Panics
133 /// Panics if `prec` is zero, or if `rm` is `Exact` but the string's value is not exactly
134 /// representable with `prec` bits.
135 ///
136 /// # Examples
137 /// ```
138 /// use core::cmp::Ordering::*;
139 /// use malachite_base::rounding_modes::RoundingMode::*;
140 /// use malachite_float::Float;
141 ///
142 /// let s = |s, prec, rm| {
143 /// Float::from_sci_string_prec_round(s, prec, rm).map(|(x, o)| (x.to_string(), o))
144 /// };
145 ///
146 /// assert_eq!(s("1.5", 10, Nearest), Some(("1.5000".to_string(), Equal)));
147 ///
148 /// // 0.1 is not representable in binary, so it is rounded and the `Ordering` gives the
149 /// // direction.
150 /// assert_eq!(s("0.1", 4, Floor), Some(("0.0938".to_string(), Less)));
151 /// assert_eq!(s("0.1", 4, Ceiling), Some(("0.102".to_string(), Greater)));
152 ///
153 /// assert_eq!(s("abc", 10, Nearest), None);
154 /// ```
155 pub fn from_sci_string_prec_round(
156 s: &str,
157 prec: u64,
158 rm: RoundingMode,
159 ) -> Option<(Self, Ordering)> {
160 let mut options = FromSciStringOptions::default();
161 options.set_rounding_mode(rm);
162 Self::from_sci_string_with_options_prec(s, options, prec)
163 }
164
165 /// Converts a string, possibly in scientific notation, to a [`Float`], with a given precision,
166 /// rounding to nearest.
167 ///
168 /// This is [`from_sci_string_prec_round`](Float::from_sci_string_prec_round) with `Nearest`.
169 ///
170 /// # Worst-case complexity
171 /// $T(n) = O(n (\log n)^2 \log\log n)$
172 ///
173 /// $M(n) = O(n \log n)$
174 ///
175 /// where $T$ is time, $M$ is additional memory, and $n$ is `max(s.len(), prec)`.
176 ///
177 /// # Panics
178 /// Panics if `prec` is zero.
179 ///
180 /// # Examples
181 /// ```
182 /// use core::cmp::Ordering::*;
183 /// use malachite_float::Float;
184 ///
185 /// let s = |s, prec| Float::from_sci_string_prec(s, prec).map(|(x, o)| (x.to_string(), o));
186 ///
187 /// assert_eq!(s("1.5", 10), Some(("1.5000".to_string(), Equal)));
188 /// assert_eq!(s("0.1", 4), Some(("0.102".to_string(), Greater)));
189 /// assert_eq!(
190 /// s("1e10", 53),
191 /// Some(("10000000000.000000".to_string(), Equal))
192 /// );
193 /// assert_eq!(s("abc", 10), None);
194 /// ```
195 #[inline]
196 pub fn from_sci_string_prec(s: &str, prec: u64) -> Option<(Self, Ordering)> {
197 Self::from_sci_string_with_options_prec(s, FromSciStringOptions::default(), prec)
198 }
199
200 /// Converts a string, possibly in scientific notation, to a [`Float`], with a given precision,
201 /// using the given options for the base and the rounding mode.
202 ///
203 /// The result is the string's exact value rounded once to `prec` bits, together with the
204 /// [`Ordering`] of the result against that exact value. `None` means the string is not a
205 /// number; it never means the value is out of range, since a value too large in magnitude gives
206 /// an infinity (or, under a mode that rounds toward zero, the largest finite value) and one too
207 /// small gives a zero.
208 ///
209 /// Use [`FromSciStringOptions`] to specify the base, from 2 to 36 inclusive, and the rounding
210 /// mode. This is the grammar the rest of Malachite uses, so a [`Float`] reads a string the same
211 /// way a [`Rational`](malachite_q::Rational) does, with three additions: the strings `NaN`,
212 /// `Infinity`, and `-Infinity`, which are what [`Float`]'s [`Display`](std::fmt::Display)
213 /// writes and are read in every base; a signed zero, so that `-0.0` is negative zero rather
214 /// than zero; and the precision and rounding mode. Note that from base 24 up `NaN` is also a
215 /// valid digit string, and from base 35 up so is `Infinity`; the special value wins.
216 ///
217 /// If the base is greater than 10, the higher digits are represented by the letters `'a'`
218 /// through `'z'` or `'A'` through `'Z'`; the case doesn't matter and doesn't need to be
219 /// consistent.
220 ///
221 /// Exponents are allowed, and are indicated using the character `'e'` or `'E'`. If the base is
222 /// 15 or greater, an ambiguity arises where it may not be clear whether `'e'` is a digit or an
223 /// exponent indicator. To resolve this ambiguity, always use a `'+'` or `'-'` sign after the
224 /// exponent indicator when the base is 15 or greater. The exponent itself is always parsed
225 /// using base 10.
226 ///
227 /// Points are allowed.
228 ///
229 /// # Worst-case complexity
230 /// $T(n) = O(n (\log n)^2 \log\log n)$
231 ///
232 /// $M(n) = O(n \log n)$
233 ///
234 /// where $T$ is time, $M$ is additional memory, and $n$ is `max(s.len(), prec)`.
235 ///
236 /// # Panics
237 /// Panics if `prec` is zero, or if the rounding mode is `Exact` but the string's value is not
238 /// exactly representable with `prec` bits.
239 ///
240 /// # Examples
241 /// ```
242 /// use core::cmp::Ordering::*;
243 /// use malachite_base::num::conversion::string::options::FromSciStringOptions;
244 /// use malachite_base::rounding_modes::RoundingMode::*;
245 /// use malachite_float::Float;
246 ///
247 /// let s = |s, options, prec| {
248 /// Float::from_sci_string_with_options_prec(s, options, prec)
249 /// .map(|(x, o)| (x.to_string(), o))
250 /// };
251 ///
252 /// let mut options = FromSciStringOptions::default();
253 /// options.set_base(16);
254 /// assert_eq!(
255 /// s("ff", options, 53),
256 /// Some(("255.00000000000000".to_string(), Equal))
257 /// );
258 /// // From base 15 up, an exponent needs an explicit sign, since `e` is also a digit.
259 /// assert_eq!(
260 /// s("1e5", options, 20),
261 /// Some(("485.00000".to_string(), Equal))
262 /// );
263 /// assert_eq!(
264 /// s("1e+5", options, 20),
265 /// Some(("1048576.0".to_string(), Equal))
266 /// );
267 ///
268 /// // The rounding mode comes from the options.
269 /// options.set_base(10);
270 /// options.set_rounding_mode(Floor);
271 /// assert_eq!(s("0.1", options, 4), Some(("0.0938".to_string(), Less)));
272 /// options.set_rounding_mode(Ceiling);
273 /// assert_eq!(s("0.1", options, 4), Some(("0.102".to_string(), Greater)));
274 ///
275 /// // Zero keeps its sign, and an exponent too large to represent gives an infinity.
276 /// assert_eq!(s("-0.0", options, 53), Some(("-0.0".to_string(), Equal)));
277 /// assert_eq!(
278 /// s("1e1000000000000000000", options, 53),
279 /// Some(("Infinity".to_string(), Greater))
280 /// );
281 /// ```
282 pub fn from_sci_string_with_options_prec(
283 s: &str,
284 options: FromSciStringOptions,
285 prec: u64,
286 ) -> Option<(Self, Ordering)> {
287 assert_ne!(prec, 0);
288 let rm = options.get_rounding_mode();
289 Some(match parse(s, options)? {
290 Parsed::Special(x) => (x, Equal),
291 Parsed::Zero(sign) => (
292 if sign {
293 Self::ZERO
294 } else {
295 Self::NEGATIVE_ZERO
296 },
297 Equal,
298 ),
299 Parsed::Overflow(sign) => overflow(sign, prec, rm),
300 Parsed::Finite(sign, digits, exp_base, _) => {
301 set_str_helper(sign, &digits, options.get_base(), exp_base, 0, prec, rm)
302 }
303 })
304 }
305}
306
307// The base prefix `to_string.rs` writes for a power-of-two base, if any.
308const fn base_prefix(base: u8) -> &'static str {
309 match base {
310 2 => "0b",
311 8 => "0o",
312 16 => "0x",
313 _ => "",
314 }
315}
316
317pub_crate_test! {
318// Reads what `ComparableFloat` writes in the given base: an optional sign, an optional base prefix,
319// the digits, and an optional `#` and precision. Without the suffix the precision is inferred from
320// the digits.
321float_from_string_base(base: u8, s: &str) -> Option<Float> {
322 // Built first, so that an invalid base is rejected whatever the string is; otherwise the
323 // strings that fail below would quietly return `None` for a base that cannot exist.
324 let mut options = FromSciStringOptions::default();
325 options.set_base(base);
326 let (body, prec) = match s.rfind('#') {
327 Some(i) => (&s[..i], Some(u64::from_str(&s[i + 1..]).ok()?)),
328 None => (s, None),
329 };
330 // A precision of zero is not a `Float` precision, and the specials and zeros never carry one.
331 if prec == Some(0) {
332 return None;
333 }
334 let (sign, after_sign) = match body.strip_prefix('-') {
335 Some(rest) => ("-", rest),
336 None => ("", body),
337 };
338 // The prefix is optional: `{:x}` omits it and `{:#x}` writes it.
339 let prefix = base_prefix(base);
340 let digits = if prefix.is_empty() {
341 after_sign
342 } else {
343 after_sign.strip_prefix(prefix).unwrap_or(after_sign)
344 };
345 // `from_sci_string` reads the sign itself, and the specials only in their unprefixed spelling.
346 let rebuilt = if sign.is_empty() && prefix.is_empty() {
347 Cow::Borrowed(body)
348 } else {
349 Cow::Owned(format!("{sign}{digits}"))
350 };
351 match prec {
352 Some(prec) => {
353 let x = Float::from_sci_string_with_options_prec(&rebuilt, options, prec)?.0;
354 // A suffix that a special or a zero cannot carry is not something they wrote.
355 x.get_prec().map(|_| x)
356 }
357 None => Float::from_sci_string_with_options(&rebuilt, options),
358 }
359}}
360
361impl FromSciString for Float {
362 /// Converts a string, possibly in scientific notation, to a [`Float`], inferring a precision
363 /// from the number of digits.
364 ///
365 /// The grammar, the base, and the treatment of the special values and of zero are as in
366 /// [`from_sci_string_with_options_prec`](Float::from_sci_string_with_options_prec), which this
367 /// differs from only in where the precision comes from. The rounding mode option is ignored;
368 /// the value is rounded to nearest.
369 ///
370 /// A string does not say how precise it is, so the precision has to be guessed, and the guess
371 /// is that its $n$ significant digits are all meaningful: $\lceil n \log_2 b \rceil$ bits. If
372 /// the value is exactly representable in fewer bits than that, it is stored in the fewest that
373 /// represent it, which makes a literal agree with [`Float::from`]: `"1.5"` gives precision 2
374 /// and `"255"` gives 8, matching `Float::from(1.5)` and `Float::from(255)`.
375 ///
376 /// This is worth dwelling on, because for short strings the guess is coarse in a way that may
377 /// surprise, much as
378 /// [`from_sci_string_simplest`](malachite_q::Rational::from_sci_string_simplest) does for
379 /// [`Rational`](malachite_q::Rational). One decimal digit buys only four bits, so `"0.1"` gives
380 /// a precision-4 [`Float`] whose value is $13/128$, or 0.1015625 — not the nearest `f64` to
381 /// 0.1, and not close to it. Reading `"0.1000000000000000055511151231257827"` gives that
382 /// instead, and asking for a precision outright with
383 /// [`from_sci_string_prec`](Float::from_sci_string_prec) avoids the question altogether. Note
384 /// also that a string with a huge exponent is not thereby precise: `"1e100000000"` still has
385 /// one significant digit, so it gives four bits rather than its exact 332 million.
386 ///
387 /// # Worst-case complexity
388 /// $T(n) = O(n (\log n)^2 \log\log n)$
389 ///
390 /// $M(n) = O(n \log n)$
391 ///
392 /// where $T$ is time, $M$ is additional memory, and $n$ is `s.len()`.
393 ///
394 /// # Examples
395 /// ```
396 /// use malachite_base::num::conversion::string::options::FromSciStringOptions;
397 /// use malachite_base::num::conversion::traits::FromSciString;
398 /// use malachite_float::Float;
399 ///
400 /// // An exactly representable value is stored in the fewest bits that represent it, so these
401 /// // agree with `Float::from`.
402 /// assert_eq!(Float::from_sci_string("1.5").unwrap(), Float::from(1.5));
403 /// assert_eq!(Float::from_sci_string("255").unwrap(), Float::from(255));
404 ///
405 /// // A value that is not exactly representable keeps the precision its digits imply, which
406 /// // for short strings is coarse: one decimal digit buys only four bits.
407 /// assert_eq!(Float::from_sci_string("0.1").unwrap().to_string(), "0.102");
408 /// assert_eq!(
409 /// Float::from_sci_string("3.14159").unwrap().to_string(),
410 /// "3.1415901"
411 /// );
412 ///
413 /// // A huge exponent does not make the digits more precise.
414 /// assert_eq!(
415 /// Float::from_sci_string("1e100000000").unwrap().to_string(),
416 /// "9.80e99999999"
417 /// );
418 ///
419 /// let mut options = FromSciStringOptions::default();
420 /// options.set_base(16);
421 /// assert_eq!(
422 /// Float::from_sci_string_with_options("ff", options).unwrap(),
423 /// Float::from(255)
424 /// );
425 ///
426 /// assert!(Float::from_sci_string("abc").is_none());
427 /// ```
428 fn from_sci_string_with_options(s: &str, options: FromSciStringOptions) -> Option<Self> {
429 let base = options.get_base();
430 Some(match parse(s, options)? {
431 Parsed::Special(x) => x,
432 Parsed::Zero(sign) => {
433 if sign {
434 Self::ZERO
435 } else {
436 Self::NEGATIVE_ZERO
437 }
438 }
439 // This is `overflow(sign, _, Nearest)`, which is an infinity whatever the precision.
440 // That is as well, since the digits cannot pin one down: an overflowing exponent says
441 // nothing about how many of them there were.
442 Parsed::Overflow(sign) => {
443 if sign {
444 Self::INFINITY
445 } else {
446 Self::NEGATIVE_INFINITY
447 }
448 }
449 Parsed::Finite(sign, digits, exp_base, significant) => {
450 let prec = implied_prec(significant, base);
451 let (x, o) = set_str_helper(sign, &digits, base, exp_base, 0, prec, Nearest);
452 // The implied precision is an upper bound on what the digits can say; when the
453 // value needs fewer bits than that, it is stored with the fewest that represent it,
454 // matching `Float::from` for a primitive float. A rounded value is not shrunk: its
455 // low bits are not the string's.
456 //
457 // An exact result here is never zero, so there is no need to guard the shrink
458 // against one: the digits are nonempty with their trailing zeros stripped, so they
459 // name a value of at least one, and the only way `set_str_helper` reaches zero is
460 // by underflowing, which is never exact.
461 if o == Equal {
462 let min_prec = (&x).integer_mantissa().significant_bits();
463 Self::from_float_prec_round(x, min_prec, Exact).0
464 } else {
465 x
466 }
467 }
468 })
469 }
470}