Deduplicate significand computation

This commit is contained in:
David Tolnay 2020-06-07 19:37:42 -07:00
parent 9ca1f555e3
commit 24fae937ea
No known key found for this signature in database
GPG Key ID: F9BA143B95FF6D82
5 changed files with 295 additions and 168 deletions

196
src/de.rs
View File

@ -381,20 +381,6 @@ impl<'de, R: Read<'de>> Deserializer<R> {
}
};
// To deserialize floats we'll first push the integer and fraction
// parts, both as byte strings, into the scratch buffer and then feed
// both slices to lexical's parser. For example if the input is
// `12.34e5` we'll push b"1234" into scratch and then pass b"12" and
// b"34" to lexical. `integer_end` will be used to track where to split
// the scratch buffer.
//
// Note that lexical expects the integer part to contain *no* leading
// zeroes and the fraction part to contain *no* trailing zeroes. The
// first requirement is already handled by the integer parsing logic.
// The second requirement will be enforced just before passing the
// slices to lexical in f64_from_parts.
self.scratch.clear();
match next {
b'0' => {
// There can be only one leading '0'.
@ -404,14 +390,11 @@ impl<'de, R: Read<'de>> Deserializer<R> {
}
}
c @ b'1'..=b'9' => {
self.scratch.push(c);
let mut significand = (c - b'0') as u64;
loop {
match tri!(self.peek_or_null()) {
c @ b'0'..=b'9' => {
self.scratch.push(c);
self.eat_char();
let digit = (c - b'0') as u64;
// We need to be careful with overflow. If we can,
@ -420,10 +403,11 @@ impl<'de, R: Read<'de>> Deserializer<R> {
// value as a `f64`.
if overflow!(significand * 10 + digit, u64::max_value()) {
return Ok(ParserNumber::F64(tri!(
self.parse_long_integer(positive),
self.parse_long_integer(positive, significand),
)));
}
self.eat_char();
significand = significand * 10 + digit;
}
_ => {
@ -437,10 +421,9 @@ impl<'de, R: Read<'de>> Deserializer<R> {
}
fn parse_number(&mut self, positive: bool, significand: u64) -> Result<ParserNumber> {
let integer_end = self.scratch.len();
Ok(match tri!(self.peek_or_null()) {
b'.' => ParserNumber::F64(tri!(self.parse_decimal(positive, integer_end))),
b'e' | b'E' => ParserNumber::F64(tri!(self.parse_exponent(positive, integer_end))),
b'.' => ParserNumber::F64(tri!(self.parse_decimal(positive, significand))),
b'e' | b'E' => ParserNumber::F64(tri!(self.parse_exponent(positive, significand, 0))),
_ => {
if positive {
ParserNumber::U64(significand)
@ -458,17 +441,28 @@ impl<'de, R: Read<'de>> Deserializer<R> {
})
}
fn parse_decimal(&mut self, positive: bool, integer_end: usize) -> Result<f64> {
fn parse_decimal(&mut self, positive: bool, mut significand: u64) -> Result<f64> {
self.eat_char();
let mut at_least_one_digit = false;
let mut exponent: i32 = 0;
while let c @ b'0'..=b'9' = tri!(self.peek_or_null()) {
self.scratch.push(c);
let digit = (c - b'0') as u64;
if overflow!(significand * 10 + digit, u64::max_value()) {
self.scratch.clear();
self.scratch
.extend_from_slice(itoa::Buffer::new().format(significand).as_bytes());
let integer_end = self.scratch.len() - (-exponent) as usize;
return self.parse_long_decimal(positive, integer_end);
}
self.eat_char();
at_least_one_digit = true;
significand = significand * 10 + digit;
exponent -= 1;
}
if !at_least_one_digit {
// Error if there is not at least one digit after the decimal point.
if exponent == 0 {
match tri!(self.peek()) {
Some(_) => return Err(self.peek_error(ErrorCode::InvalidNumber)),
None => return Err(self.peek_error(ErrorCode::EofWhileParsingValue)),
@ -476,12 +470,17 @@ impl<'de, R: Read<'de>> Deserializer<R> {
}
match tri!(self.peek_or_null()) {
b'e' | b'E' => self.parse_exponent(positive, integer_end),
_ => self.f64_from_parts(positive, integer_end, 0),
b'e' | b'E' => self.parse_exponent(positive, significand, exponent),
_ => self.f64_from_parts(positive, significand, exponent),
}
}
fn parse_exponent(&mut self, positive: bool, integer_end: usize) -> Result<f64> {
fn parse_exponent(
&mut self,
positive: bool,
significand: u64,
starting_exp: i32,
) -> Result<f64> {
self.eat_char();
let positive_exp = match tri!(self.peek_or_null()) {
@ -516,18 +515,54 @@ impl<'de, R: Read<'de>> Deserializer<R> {
let digit = (c - b'0') as i32;
if overflow!(exp * 10 + digit, i32::max_value()) {
return self.parse_exponent_overflow(positive, positive_exp);
let zero_significand = significand == 0;
return self.parse_exponent_overflow(positive, zero_significand, positive_exp);
}
exp = exp * 10 + digit;
}
let final_exp = if positive_exp { exp } else { -exp };
let final_exp = if positive_exp {
starting_exp.saturating_add(exp)
} else {
starting_exp.saturating_sub(exp)
};
self.f64_from_parts(positive, integer_end, final_exp)
self.f64_from_parts(positive, significand, final_exp)
}
fn parse_long_integer(&mut self, positive: bool) -> Result<f64> {
fn f64_from_parts(&mut self, positive: bool, significand: u64, exponent: i32) -> Result<f64> {
let f = if self.single_precision {
lexical::parse_concise_float::<f32>(significand, exponent) as f64
} else {
lexical::parse_concise_float::<f64>(significand, exponent)
};
if f.is_infinite() {
Err(self.error(ErrorCode::NumberOutOfRange))
} else {
Ok(if positive { f } else { -f })
}
}
#[cold]
fn parse_long_integer(&mut self, positive: bool, partial_significand: u64) -> Result<f64> {
// To deserialize floats we'll first push the integer and fraction
// parts, both as byte strings, into the scratch buffer and then feed
// both slices to lexical's parser. For example if the input is
// `12.34e5` we'll push b"1234" into scratch and then pass b"12" and
// b"34" to lexical. `integer_end` will be used to track where to split
// the scratch buffer.
//
// Note that lexical expects the integer part to contain *no* leading
// zeroes and the fraction part to contain *no* trailing zeroes. The
// first requirement is already handled by the integer parsing logic.
// The second requirement will be enforced just before passing the
// slices to lexical in f64_long_from_parts.
self.scratch.clear();
self.scratch
.extend_from_slice(itoa::Buffer::new().format(partial_significand).as_bytes());
loop {
match tri!(self.peek_or_null()) {
c @ b'0'..=b'9' => {
@ -535,25 +570,99 @@ impl<'de, R: Read<'de>> Deserializer<R> {
self.eat_char();
}
b'.' => {
return self.parse_decimal(positive, self.scratch.len());
self.eat_char();
return self.parse_long_decimal(positive, self.scratch.len());
}
b'e' | b'E' => {
return self.parse_exponent(positive, self.scratch.len());
return self.parse_long_exponent(positive, self.scratch.len());
}
_ => {
return self.f64_from_parts(positive, self.scratch.len(), 0);
return self.f64_long_from_parts(positive, self.scratch.len(), 0);
}
}
}
}
fn parse_long_decimal(&mut self, positive: bool, integer_end: usize) -> Result<f64> {
let mut at_least_one_digit = integer_end < self.scratch.len();
while let c @ b'0'..=b'9' = tri!(self.peek_or_null()) {
self.scratch.push(c);
self.eat_char();
at_least_one_digit = true;
}
if !at_least_one_digit {
match tri!(self.peek()) {
Some(_) => return Err(self.peek_error(ErrorCode::InvalidNumber)),
None => return Err(self.peek_error(ErrorCode::EofWhileParsingValue)),
}
}
match tri!(self.peek_or_null()) {
b'e' | b'E' => self.parse_long_exponent(positive, integer_end),
_ => self.f64_long_from_parts(positive, integer_end, 0),
}
}
fn parse_long_exponent(&mut self, positive: bool, integer_end: usize) -> Result<f64> {
self.eat_char();
let positive_exp = match tri!(self.peek_or_null()) {
b'+' => {
self.eat_char();
true
}
b'-' => {
self.eat_char();
false
}
_ => true,
};
let next = match tri!(self.next_char()) {
Some(b) => b,
None => {
return Err(self.error(ErrorCode::EofWhileParsingValue));
}
};
// Make sure a digit follows the exponent place.
let mut exp = match next {
c @ b'0'..=b'9' => (c - b'0') as i32,
_ => {
return Err(self.error(ErrorCode::InvalidNumber));
}
};
while let c @ b'0'..=b'9' = tri!(self.peek_or_null()) {
self.eat_char();
let digit = (c - b'0') as i32;
if overflow!(exp * 10 + digit, i32::max_value()) {
let zero_significand = self.scratch.iter().all(|&digit| digit == b'0');
return self.parse_exponent_overflow(positive, zero_significand, positive_exp);
}
exp = exp * 10 + digit;
}
let final_exp = if positive_exp { exp } else { -exp };
self.f64_long_from_parts(positive, integer_end, final_exp)
}
// This cold code should not be inlined into the middle of the hot
// exponent-parsing loop above.
#[cold]
#[inline(never)]
fn parse_exponent_overflow(&mut self, positive: bool, positive_exp: bool) -> Result<f64> {
fn parse_exponent_overflow(
&mut self,
positive: bool,
zero_significand: bool,
positive_exp: bool,
) -> Result<f64> {
// Error instead of +/- infinity.
if positive_exp && !self.scratch.iter().all(|&digit| digit == b'0') {
if !zero_significand && positive_exp {
return Err(self.error(ErrorCode::NumberOutOfRange));
}
@ -563,14 +672,19 @@ impl<'de, R: Read<'de>> Deserializer<R> {
Ok(if positive { 0.0 } else { -0.0 })
}
fn f64_from_parts(&mut self, positive: bool, integer_end: usize, exponent: i32) -> Result<f64> {
fn f64_long_from_parts(
&mut self,
positive: bool,
integer_end: usize,
exponent: i32,
) -> Result<f64> {
let integer = &self.scratch[..integer_end];
let fraction = &self.scratch[integer_end..];
let f = if self.single_precision {
lexical::parse_float::<f32>(integer, fraction, exponent) as f64
lexical::parse_truncated_float::<f32>(integer, fraction, exponent) as f64
} else {
lexical::parse_float::<f64>(integer, fraction, exponent)
lexical::parse_truncated_float::<f64>(integer, fraction, exponent)
};
if f.is_infinite() {

View File

@ -24,7 +24,9 @@ where
let (min_exp, max_exp) = F::exponent_limit();
let shift_exp = F::mantissa_limit();
let mantissa_size = F::MANTISSA_SIZE + 1;
if mantissa >> mantissa_size != 0 {
if mantissa == 0 {
Some(F::ZERO)
} else if mantissa >> mantissa_size != 0 {
// Would require truncation of the mantissa.
None
} else if exponent == 0 {

View File

@ -35,5 +35,4 @@ mod large_powers32;
mod large_powers64;
// API
pub use self::num::Float;
pub use self::parse::parse_float;
pub use self::parse::{parse_concise_float, parse_truncated_float};

View File

@ -1,6 +1,7 @@
// Adapted from https://github.com/Alexhuszagh/rust-lexical.
use super::algorithm::*;
use super::bhcomp::*;
use super::digit::*;
use super::exponent::*;
use super::num::*;
@ -8,28 +9,34 @@ use super::num::*;
// PARSERS
// -------
/// Parse the significant digits of the float.
///
/// * `integer` - Slice containing the integer digits.
/// * `fraction` - Slice containing the fraction digits.
fn parse_mantissa(integer: &[u8], fraction: &[u8]) -> (u64, usize) {
let mut value: u64 = 0;
// On overflow, calculate the number of truncated digits.
let mut integer = integer.iter();
while let Some(c) = integer.next() {
value = match add_digit(value, to_digit(*c).unwrap()) {
Some(v) => v,
None => return (value, 1 + integer.count() + fraction.len()),
};
/// Parse float for which the entire integer and fraction parts fit into a 64
/// bit mantissa.
pub fn parse_concise_float<F>(mantissa: u64, mant_exp: i32) -> F
where
F: Float,
{
if let Some(float) = fast_path(mantissa, mant_exp) {
return float;
}
let mut fraction = fraction.iter();
while let Some(c) = fraction.next() {
value = match add_digit(value, to_digit(*c).unwrap()) {
Some(v) => v,
None => return (value, 1 + fraction.count()),
};
// Moderate path (use an extended 80-bit representation).
let truncated = false;
let (fp, valid) = moderate_path::<F>(mantissa, mant_exp, truncated);
if valid {
return fp.into_float::<F>();
}
(value, 0)
let b = fp.into_downward_float::<F>();
if b.is_special() {
// We have a non-finite number, we get to leave early.
return b;
}
// Slow path, fast path didn't work.
let mut buffer = itoa::Buffer::new();
let integer = buffer.format(mantissa).as_bytes();
let fraction = &[];
bhcomp(b, integer, fraction, mant_exp)
}
/// Parse float from extracted float components.
@ -39,7 +46,7 @@ fn parse_mantissa(integer: &[u8], fraction: &[u8]) -> (u64, usize) {
/// * `exponent` - Parsed, 32-bit exponent.
///
/// Precondition: The integer must not have leading zeros.
pub fn parse_float<F>(integer: &[u8], mut fraction: &[u8], exponent: i32) -> F
pub fn parse_truncated_float<F>(integer: &[u8], mut fraction: &[u8], exponent: i32) -> F
where
F: Float,
{
@ -48,24 +55,22 @@ where
fraction = &fraction[..fraction.len() - 1];
}
// Parse the mantissa and attempt the fast and moderate-path algorithms.
let (mantissa, truncated) = parse_mantissa(integer, fraction);
if mantissa == 0 {
// Literal 0, return early. Value cannot be truncated since truncation
// only occurs on overflow or underflow.
return F::ZERO;
// Calculate the number of truncated digits.
let mut truncated = 0;
let mut mantissa: u64 = 0;
let mut iter = integer.iter().chain(fraction);
for &c in &mut iter {
mantissa = match add_digit(mantissa, to_digit(c).unwrap()) {
Some(v) => v,
None => {
truncated = 1 + iter.count();
break;
}
};
}
let mant_exp = mantissa_exponent(exponent, fraction.len(), truncated);
// Try the fast path if no mantissa truncation.
let is_truncated = truncated != 0;
if !is_truncated {
if let Some(float) = fast_path(mantissa, mant_exp) {
return float;
}
}
let is_truncated = true;
fallback_path(
integer,

View File

@ -1,99 +1,106 @@
// Adapted from https://github.com/Alexhuszagh/rust-lexical.
use crate::lexical::num::Float;
use crate::lexical::parse::parse_float;
use crate::lexical::parse::{parse_concise_float, parse_truncated_float};
use core::f64;
use core::fmt::Debug;
fn check_parse_float<F>(integer: &str, fraction: &str, exponent: i32, expected: F)
fn check_concise_float<F>(mantissa: u64, exponent: i32, expected: F)
where
F: Float + Debug,
{
assert_eq!(parse_concise_float::<F>(mantissa, exponent), expected);
}
fn check_truncated_float<F>(integer: &str, fraction: &str, exponent: i32, expected: F)
where
F: Float + Debug,
{
let integer = integer.as_bytes();
let fraction = fraction.as_bytes();
assert_eq!(parse_float::<F>(integer, fraction, exponent), expected);
assert_eq!(parse_truncated_float::<F>(integer, fraction, exponent), expected);
}
#[test]
fn parse_f32_test() {
check_parse_float("", "", 0, 0.0_f32);
check_parse_float("1", "2345", 0, 1.2345_f32);
check_parse_float("12", "345", 0, 12.345_f32);
check_parse_float("12345", "6789", 0, 12345.6789_f32);
check_parse_float("1", "2345", 10, 1.2345e10_f32);
check_parse_float("1", "2345", -38, 1.2345e-38_f32);
check_concise_float(0, 0, 0.0_f32);
check_concise_float(12345, -4, 1.2345_f32);
check_concise_float(12345, -3, 12.345_f32);
check_concise_float(123456789, -4, 12345.6789_f32);
check_concise_float(12345, 6, 1.2345e10_f32);
check_concise_float(12345, -42, 1.2345e-38_f32);
// Check expected rounding, using borderline cases.
// Round-down, halfway
check_parse_float("16777216", "", 0, 16777216.0_f32);
check_parse_float("16777217", "", 0, 16777216.0_f32);
check_parse_float("16777218", "", 0, 16777218.0_f32);
check_parse_float("33554432", "", 0, 33554432.0_f32);
check_parse_float("33554434", "", 0, 33554432.0_f32);
check_parse_float("33554436", "", 0, 33554436.0_f32);
check_parse_float("17179869184", "", 0, 17179869184.0_f32);
check_parse_float("17179870208", "", 0, 17179869184.0_f32);
check_parse_float("17179871232", "", 0, 17179871232.0_f32);
check_concise_float(16777216, 0, 16777216.0_f32);
check_concise_float(16777217, 0, 16777216.0_f32);
check_concise_float(16777218, 0, 16777218.0_f32);
check_concise_float(33554432, 0, 33554432.0_f32);
check_concise_float(33554434, 0, 33554432.0_f32);
check_concise_float(33554436, 0, 33554436.0_f32);
check_concise_float(17179869184, 0, 17179869184.0_f32);
check_concise_float(17179870208, 0, 17179869184.0_f32);
check_concise_float(17179871232, 0, 17179871232.0_f32);
// Round-up, halfway
check_parse_float("16777218", "", 0, 16777218.0_f32);
check_parse_float("16777219", "", 0, 16777220.0_f32);
check_parse_float("16777220", "", 0, 16777220.0_f32);
check_concise_float(16777218, 0, 16777218.0_f32);
check_concise_float(16777219, 0, 16777220.0_f32);
check_concise_float(16777220, 0, 16777220.0_f32);
check_parse_float("33554436", "", 0, 33554436.0_f32);
check_parse_float("33554438", "", 0, 33554440.0_f32);
check_parse_float("33554440", "", 0, 33554440.0_f32);
check_concise_float(33554436, 0, 33554436.0_f32);
check_concise_float(33554438, 0, 33554440.0_f32);
check_concise_float(33554440, 0, 33554440.0_f32);
check_parse_float("17179871232", "", 0, 17179871232.0_f32);
check_parse_float("17179872256", "", 0, 17179873280.0_f32);
check_parse_float("17179873280", "", 0, 17179873280.0_f32);
check_concise_float(17179871232, 0, 17179871232.0_f32);
check_concise_float(17179872256, 0, 17179873280.0_f32);
check_concise_float(17179873280, 0, 17179873280.0_f32);
// Round-up, above halfway
check_parse_float("33554435", "", 0, 33554436.0_f32);
check_parse_float("17179870209", "", 0, 17179871232.0_f32);
check_concise_float(33554435, 0, 33554436.0_f32);
check_concise_float(17179870209, 0, 17179871232.0_f32);
// Check exactly halfway, round-up at halfway
check_parse_float("1", "00000017881393432617187499", 0, 1.0000001_f32);
check_parse_float("1", "000000178813934326171875", 0, 1.0000002_f32);
check_parse_float("1", "00000017881393432617187501", 0, 1.0000002_f32);
check_truncated_float("1", "00000017881393432617187499", 0, 1.0000001_f32);
check_truncated_float("1", "000000178813934326171875", 0, 1.0000002_f32);
check_truncated_float("1", "00000017881393432617187501", 0, 1.0000002_f32);
}
#[test]
fn parse_f64_test() {
check_parse_float("", "", 0, 0.0_f64);
check_parse_float("1", "2345", 0, 1.2345_f64);
check_parse_float("12", "345", 0, 12.345_f64);
check_parse_float("12345", "6789", 0, 12345.6789_f64);
check_parse_float("1", "2345", 10, 1.2345e10_f64);
check_parse_float("1", "2345", -308, 1.2345e-308_f64);
check_concise_float(0, 0, 0.0_f64);
check_concise_float(12345, -4, 1.2345_f64);
check_concise_float(12345, -3, 12.345_f64);
check_concise_float(123456789, -4, 12345.6789_f64);
check_concise_float(12345, 6, 1.2345e10_f64);
check_concise_float(12345, -312, 1.2345e-308_f64);
// Check expected rounding, using borderline cases.
// Round-down, halfway
check_parse_float("9007199254740992", "", 0, 9007199254740992.0_f64);
check_parse_float("9007199254740993", "", 0, 9007199254740992.0_f64);
check_parse_float("9007199254740994", "", 0, 9007199254740994.0_f64);
check_concise_float(9007199254740992, 0, 9007199254740992.0_f64);
check_concise_float(9007199254740993, 0, 9007199254740992.0_f64);
check_concise_float(9007199254740994, 0, 9007199254740994.0_f64);
check_parse_float("18014398509481984", "", 0, 18014398509481984.0_f64);
check_parse_float("18014398509481986", "", 0, 18014398509481984.0_f64);
check_parse_float("18014398509481988", "", 0, 18014398509481988.0_f64);
check_concise_float(18014398509481984, 0, 18014398509481984.0_f64);
check_concise_float(18014398509481986, 0, 18014398509481984.0_f64);
check_concise_float(18014398509481988, 0, 18014398509481988.0_f64);
check_parse_float("9223372036854775808", "", 0, 9223372036854775808.0_f64);
check_parse_float("9223372036854776832", "", 0, 9223372036854775808.0_f64);
check_parse_float("9223372036854777856", "", 0, 9223372036854777856.0_f64);
check_concise_float(9223372036854775808, 0, 9223372036854775808.0_f64);
check_concise_float(9223372036854776832, 0, 9223372036854775808.0_f64);
check_concise_float(9223372036854777856, 0, 9223372036854777856.0_f64);
check_parse_float(
check_truncated_float(
"11417981541647679048466287755595961091061972992",
"",
0,
11417981541647679048466287755595961091061972992.0_f64,
);
check_parse_float(
check_truncated_float(
"11417981541647680316116887983825362587765178368",
"",
0,
11417981541647679048466287755595961091061972992.0_f64,
);
check_parse_float(
check_truncated_float(
"11417981541647681583767488212054764084468383744",
"",
0,
@ -101,31 +108,31 @@ fn parse_f64_test() {
);
// Round-up, halfway
check_parse_float("9007199254740994", "", 0, 9007199254740994.0_f64);
check_parse_float("9007199254740995", "", 0, 9007199254740996.0_f64);
check_parse_float("9007199254740996", "", 0, 9007199254740996.0_f64);
check_concise_float(9007199254740994, 0, 9007199254740994.0_f64);
check_concise_float(9007199254740995, 0, 9007199254740996.0_f64);
check_concise_float(9007199254740996, 0, 9007199254740996.0_f64);
check_parse_float("18014398509481988", "", 0, 18014398509481988.0_f64);
check_parse_float("18014398509481990", "", 0, 18014398509481992.0_f64);
check_parse_float("18014398509481992", "", 0, 18014398509481992.0_f64);
check_concise_float(18014398509481988, 0, 18014398509481988.0_f64);
check_concise_float(18014398509481990, 0, 18014398509481992.0_f64);
check_concise_float(18014398509481992, 0, 18014398509481992.0_f64);
check_parse_float("9223372036854777856", "", 0, 9223372036854777856.0_f64);
check_parse_float("9223372036854778880", "", 0, 9223372036854779904.0_f64);
check_parse_float("9223372036854779904", "", 0, 9223372036854779904.0_f64);
check_concise_float(9223372036854777856, 0, 9223372036854777856.0_f64);
check_concise_float(9223372036854778880, 0, 9223372036854779904.0_f64);
check_concise_float(9223372036854779904, 0, 9223372036854779904.0_f64);
check_parse_float(
check_truncated_float(
"11417981541647681583767488212054764084468383744",
"",
0,
11417981541647681583767488212054764084468383744.0_f64,
);
check_parse_float(
check_truncated_float(
"11417981541647682851418088440284165581171589120",
"",
0,
11417981541647684119068688668513567077874794496.0_f64,
);
check_parse_float(
check_truncated_float(
"11417981541647684119068688668513567077874794496",
"",
0,
@ -133,8 +140,8 @@ fn parse_f64_test() {
);
// Round-up, above halfway
check_parse_float("9223372036854776833", "", 0, 9223372036854777856.0_f64);
check_parse_float(
check_concise_float(9223372036854776833, 0, 9223372036854777856.0_f64);
check_truncated_float(
"11417981541647680316116887983825362587765178369",
"",
0,
@ -143,44 +150,44 @@ fn parse_f64_test() {
// Rounding error
// Adapted from failures in strtod.
check_parse_float("2", "2250738585072014", -308, 2.2250738585072014e-308_f64);
check_parse_float("2", "2250738585072011360574097967091319759348195463516456480234261097248222220210769455165295239081350879141491589130396211068700864386945946455276572074078206217433799881410632673292535522868813721490129811224514518898490572223072852551331557550159143974763979834118019993239625482890171070818506906306666559949382757725720157630626906633326475653000092458883164330377797918696120494973903778297049050510806099407302629371289589500035837999672072543043602840788957717961509455167482434710307026091446215722898802581825451803257070188608721131280795122334262883686223215037756666225039825343359745688844239002654981983854879482922068947216898310996983658468140228542433306603398508864458040010349339704275671864433837704860378616227717385456230658746790140867233276367187499", -308, 2.225073858507201e-308_f64);
check_parse_float("2", "22507385850720113605740979670913197593481954635164564802342610972482222202107694551652952390813508791414915891303962110687008643869459464552765720740782062174337998814106326732925355228688137214901298112245145188984905722230728525513315575501591439747639798341180199932396254828901710708185069063066665599493827577257201576306269066333264756530000924588831643303777979186961204949739037782970490505108060994073026293712895895000358379996720725430436028407889577179615094551674824347103070260914462157228988025818254518032570701886087211312807951223342628836862232150377566662250398253433597456888442390026549819838548794829220689472168983109969836584681402285424333066033985088644580400103493397042756718644338377048603786162277173854562306587467901408672332763671875", -308, 2.2250738585072014e-308_f64);
check_parse_float("2", "2250738585072011360574097967091319759348195463516456480234261097248222220210769455165295239081350879141491589130396211068700864386945946455276572074078206217433799881410632673292535522868813721490129811224514518898490572223072852551331557550159143974763979834118019993239625482890171070818506906306666559949382757725720157630626906633326475653000092458883164330377797918696120494973903778297049050510806099407302629371289589500035837999672072543043602840788957717961509455167482434710307026091446215722898802581825451803257070188608721131280795122334262883686223215037756666225039825343359745688844239002654981983854879482922068947216898310996983658468140228542433306603398508864458040010349339704275671864433837704860378616227717385456230658746790140867233276367187501", -308, 2.2250738585072014e-308_f64);
check_parse_float("179769313486231580793728971405303415079934132710037826936173778980444968292764750946649017977587207096330286416692887910946555547851940402630657488671505820681908902000708383676273854845817711531764475730270069855571366959622842914819860834936475292719074168444365510704342711559699508093042880177904174497791", "9999999999999999999999999999999999999999999999999999999999999999999999", 0, 1.7976931348623157e+308_f64);
check_parse_float("7", "4109846876186981626485318930233205854758970392148714663837852375101326090531312779794975454245398856969484704316857659638998506553390969459816219401617281718945106978546710679176872575177347315553307795408549809608457500958111373034747658096871009590975442271004757307809711118935784838675653998783503015228055934046593739791790738723868299395818481660169122019456499931289798411362062484498678713572180352209017023903285791732520220528974020802906854021606612375549983402671300035812486479041385743401875520901590172592547146296175134159774938718574737870961645638908718119841271673056017045493004705269590165763776884908267986972573366521765567941072508764337560846003984904972149117463085539556354188641513168478436313080237596295773983001708984374999", -324, 5.0e-324_f64);
check_parse_float("7", "4109846876186981626485318930233205854758970392148714663837852375101326090531312779794975454245398856969484704316857659638998506553390969459816219401617281718945106978546710679176872575177347315553307795408549809608457500958111373034747658096871009590975442271004757307809711118935784838675653998783503015228055934046593739791790738723868299395818481660169122019456499931289798411362062484498678713572180352209017023903285791732520220528974020802906854021606612375549983402671300035812486479041385743401875520901590172592547146296175134159774938718574737870961645638908718119841271673056017045493004705269590165763776884908267986972573366521765567941072508764337560846003984904972149117463085539556354188641513168478436313080237596295773983001708984375", -324, 1.0e-323_f64);
check_parse_float("7", "4109846876186981626485318930233205854758970392148714663837852375101326090531312779794975454245398856969484704316857659638998506553390969459816219401617281718945106978546710679176872575177347315553307795408549809608457500958111373034747658096871009590975442271004757307809711118935784838675653998783503015228055934046593739791790738723868299395818481660169122019456499931289798411362062484498678713572180352209017023903285791732520220528974020802906854021606612375549983402671300035812486479041385743401875520901590172592547146296175134159774938718574737870961645638908718119841271673056017045493004705269590165763776884908267986972573366521765567941072508764337560846003984904972149117463085539556354188641513168478436313080237596295773983001708984375001", -324, 1.0e-323_f64);
check_parse_float("", "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024703282292062327208828439643411068618252990130716238221279284125033775363510437593264991818081799618989828234772285886546332835517796989819938739800539093906315035659515570226392290858392449105184435931802849936536152500319370457678249219365623669863658480757001585769269903706311928279558551332927834338409351978015531246597263579574622766465272827220056374006485499977096599470454020828166226237857393450736339007967761930577506740176324673600968951340535537458516661134223766678604162159680461914467291840300530057530849048765391711386591646239524912623653881879636239373280423891018672348497668235089863388587925628302755995657524455507255189313690836254779186948667994968324049705821028513185451396213837722826145437693412532098591327667236328125", 0, 0.0_f64);
check_concise_float(22250738585072014, -324, 2.2250738585072014e-308_f64);
check_truncated_float("2", "2250738585072011360574097967091319759348195463516456480234261097248222220210769455165295239081350879141491589130396211068700864386945946455276572074078206217433799881410632673292535522868813721490129811224514518898490572223072852551331557550159143974763979834118019993239625482890171070818506906306666559949382757725720157630626906633326475653000092458883164330377797918696120494973903778297049050510806099407302629371289589500035837999672072543043602840788957717961509455167482434710307026091446215722898802581825451803257070188608721131280795122334262883686223215037756666225039825343359745688844239002654981983854879482922068947216898310996983658468140228542433306603398508864458040010349339704275671864433837704860378616227717385456230658746790140867233276367187499", -308, 2.225073858507201e-308_f64);
check_truncated_float("2", "22507385850720113605740979670913197593481954635164564802342610972482222202107694551652952390813508791414915891303962110687008643869459464552765720740782062174337998814106326732925355228688137214901298112245145188984905722230728525513315575501591439747639798341180199932396254828901710708185069063066665599493827577257201576306269066333264756530000924588831643303777979186961204949739037782970490505108060994073026293712895895000358379996720725430436028407889577179615094551674824347103070260914462157228988025818254518032570701886087211312807951223342628836862232150377566662250398253433597456888442390026549819838548794829220689472168983109969836584681402285424333066033985088644580400103493397042756718644338377048603786162277173854562306587467901408672332763671875", -308, 2.2250738585072014e-308_f64);
check_truncated_float("2", "2250738585072011360574097967091319759348195463516456480234261097248222220210769455165295239081350879141491589130396211068700864386945946455276572074078206217433799881410632673292535522868813721490129811224514518898490572223072852551331557550159143974763979834118019993239625482890171070818506906306666559949382757725720157630626906633326475653000092458883164330377797918696120494973903778297049050510806099407302629371289589500035837999672072543043602840788957717961509455167482434710307026091446215722898802581825451803257070188608721131280795122334262883686223215037756666225039825343359745688844239002654981983854879482922068947216898310996983658468140228542433306603398508864458040010349339704275671864433837704860378616227717385456230658746790140867233276367187501", -308, 2.2250738585072014e-308_f64);
check_truncated_float("179769313486231580793728971405303415079934132710037826936173778980444968292764750946649017977587207096330286416692887910946555547851940402630657488671505820681908902000708383676273854845817711531764475730270069855571366959622842914819860834936475292719074168444365510704342711559699508093042880177904174497791", "9999999999999999999999999999999999999999999999999999999999999999999999", 0, 1.7976931348623157e+308_f64);
check_truncated_float("7", "4109846876186981626485318930233205854758970392148714663837852375101326090531312779794975454245398856969484704316857659638998506553390969459816219401617281718945106978546710679176872575177347315553307795408549809608457500958111373034747658096871009590975442271004757307809711118935784838675653998783503015228055934046593739791790738723868299395818481660169122019456499931289798411362062484498678713572180352209017023903285791732520220528974020802906854021606612375549983402671300035812486479041385743401875520901590172592547146296175134159774938718574737870961645638908718119841271673056017045493004705269590165763776884908267986972573366521765567941072508764337560846003984904972149117463085539556354188641513168478436313080237596295773983001708984374999", -324, 5.0e-324_f64);
check_truncated_float("7", "4109846876186981626485318930233205854758970392148714663837852375101326090531312779794975454245398856969484704316857659638998506553390969459816219401617281718945106978546710679176872575177347315553307795408549809608457500958111373034747658096871009590975442271004757307809711118935784838675653998783503015228055934046593739791790738723868299395818481660169122019456499931289798411362062484498678713572180352209017023903285791732520220528974020802906854021606612375549983402671300035812486479041385743401875520901590172592547146296175134159774938718574737870961645638908718119841271673056017045493004705269590165763776884908267986972573366521765567941072508764337560846003984904972149117463085539556354188641513168478436313080237596295773983001708984375", -324, 1.0e-323_f64);
check_truncated_float("7", "4109846876186981626485318930233205854758970392148714663837852375101326090531312779794975454245398856969484704316857659638998506553390969459816219401617281718945106978546710679176872575177347315553307795408549809608457500958111373034747658096871009590975442271004757307809711118935784838675653998783503015228055934046593739791790738723868299395818481660169122019456499931289798411362062484498678713572180352209017023903285791732520220528974020802906854021606612375549983402671300035812486479041385743401875520901590172592547146296175134159774938718574737870961645638908718119841271673056017045493004705269590165763776884908267986972573366521765567941072508764337560846003984904972149117463085539556354188641513168478436313080237596295773983001708984375001", -324, 1.0e-323_f64);
check_truncated_float("", "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024703282292062327208828439643411068618252990130716238221279284125033775363510437593264991818081799618989828234772285886546332835517796989819938739800539093906315035659515570226392290858392449105184435931802849936536152500319370457678249219365623669863658480757001585769269903706311928279558551332927834338409351978015531246597263579574622766465272827220056374006485499977096599470454020828166226237857393450736339007967761930577506740176324673600968951340535537458516661134223766678604162159680461914467291840300530057530849048765391711386591646239524912623653881879636239373280423891018672348497668235089863388587925628302755995657524455507255189313690836254779186948667994968324049705821028513185451396213837722826145437693412532098591327667236328125", 0, 0.0_f64);
// Rounding error
// Adapted from:
// https://www.exploringbinary.com/how-glibc-strtod-works/
check_parse_float("", "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072008890245868760858598876504231122409594654935248025624400092282356951787758888037591552642309780950434312085877387158357291821993020294379224223559819827501242041788969571311791082261043971979604000454897391938079198936081525613113376149842043271751033627391549782731594143828136275113838604094249464942286316695429105080201815926642134996606517803095075913058719846423906068637102005108723282784678843631944515866135041223479014792369585208321597621066375401613736583044193603714778355306682834535634005074073040135602968046375918583163124224521599262546494300836851861719422417646455137135420132217031370496583210154654068035397417906022589503023501937519773030945763173210852507299305089761582519159720757232455434770912461317493580281734466552734375", 0, 2.2250738585072011e-308_f64);
check_truncated_float("", "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072008890245868760858598876504231122409594654935248025624400092282356951787758888037591552642309780950434312085877387158357291821993020294379224223559819827501242041788969571311791082261043971979604000454897391938079198936081525613113376149842043271751033627391549782731594143828136275113838604094249464942286316695429105080201815926642134996606517803095075913058719846423906068637102005108723282784678843631944515866135041223479014792369585208321597621066375401613736583044193603714778355306682834535634005074073040135602968046375918583163124224521599262546494300836851861719422417646455137135420132217031370496583210154654068035397417906022589503023501937519773030945763173210852507299305089761582519159720757232455434770912461317493580281734466552734375", 0, 2.2250738585072011e-308_f64);
// Rounding error
// Adapted from test-parse-random failures.
check_parse_float("1009", "", -31, 1.009e-28_f64);
check_parse_float("18294", "", 304, f64::INFINITY);
check_concise_float(1009, -31, 1.009e-28_f64);
check_concise_float(18294, 304, f64::INFINITY);
// Rounding error
// Adapted from a @dangrabcad's issue #20.
check_parse_float("7", "689539722041643", 164, 7.689539722041643e164_f64);
check_parse_float("768953972204164300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "", 0, 7.689539722041643e164_f64);
check_parse_float("768953972204164300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "0", 0, 7.689539722041643e164_f64);
check_concise_float(7689539722041643, 149, 7.689539722041643e164_f64);
check_truncated_float("768953972204164300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "", 0, 7.689539722041643e164_f64);
check_truncated_float("768953972204164300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "0", 0, 7.689539722041643e164_f64);
// Check other cases similar to @dangrabcad's issue #20.
check_parse_float("9223372036854776833", "0", 0, 9223372036854777856.0_f64);
check_parse_float(
check_truncated_float("9223372036854776833", "0", 0, 9223372036854777856.0_f64);
check_truncated_float(
"11417981541647680316116887983825362587765178369",
"0",
0,
11417981541647681583767488212054764084468383744.0_f64,
);
check_parse_float("9007199254740995", "0", 0, 9007199254740996.0_f64);
check_parse_float("18014398509481990", "0", 0, 18014398509481992.0_f64);
check_parse_float("9223372036854778880", "0", 0, 9223372036854779904.0_f64);
check_parse_float(
check_concise_float(90071992547409950, -1, 9007199254740996.0_f64);
check_concise_float(180143985094819900, -1, 18014398509481992.0_f64);
check_truncated_float("9223372036854778880", "0", 0, 9223372036854779904.0_f64);
check_truncated_float(
"11417981541647682851418088440284165581171589120",
"0",
0,
@ -188,7 +195,7 @@ fn parse_f64_test() {
);
// Check other cases ostensibly identified via proptest.
check_parse_float("71610528364411830000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "0", 0, 71610528364411830000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0_f64);
check_parse_float("126769393745745060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "0", 0, 126769393745745060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0_f64);
check_parse_float("38652960461239320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "0", 0, 38652960461239320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0_f64);
check_truncated_float("71610528364411830000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "0", 0, 71610528364411830000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0_f64);
check_truncated_float("126769393745745060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "0", 0, 126769393745745060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0_f64);
check_truncated_float("38652960461239320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "0", 0, 38652960461239320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0_f64);
}