use crate::{
errors::{syntax_error, ParseResult},
ParserCore,
};
use super::NumberParserSettings;
pub fn parse_binary_float(
parser: &mut ParserCore,
negative: bool,
settings: &NumberParserSettings,
) -> ParseResult<f64> {
#[derive(Debug)]
enum State {
None,
Digit,
One,
Decimal,
DigitDecimal,
OneDecimal,
}
let mut state = State::None;
let mut mantissa = 0u64;
let mut exponent = 0i32;
let mut whole_empty = false;
let mut fraction_empty = false;
let loc = parser.loc();
while !parser.is_at_eof() {
let ch = parser.peek();
if settings.permit_underscores && ch == '_' {
parser.consume();
continue;
}
match state {
State::None => match ch {
'0' => {
state = State::Digit;
}
'1' => {
mantissa = 1;
state = State::One;
}
'.' => {
whole_empty = true;
fraction_empty = true;
state = State::Decimal;
}
ch if ch.is_ascii_digit() => {
parser.consume();
return Err(syntax_error(parser.loc(), "Invalid binary digit"));
}
_ => {
break;
}
},
State::Digit => match ch {
'0' => {}
'1' => {
mantissa = 1;
state = State::One;
}
'.' => {
fraction_empty = true;
state = State::DigitDecimal;
}
ch if ch.is_ascii_digit() => {
parser.consume();
return Err(syntax_error(parser.loc(), "Invalid binary digit"));
}
_ => {
break;
}
},
State::One => match ch {
'0' => {
if mantissa >= 0x8000_0000_0000_0000 {
parser.consume();
return Err(syntax_error(parser.loc(), "Too many significant digits"));
}
mantissa <<= 1;
exponent += 1;
}
'1' => {
if mantissa >= 0x8000_0000_0000_0000 {
parser.consume();
return Err(syntax_error(parser.loc(), "Too many significant digits"));
}
mantissa <<= 1;
mantissa |= 1;
exponent += 1;
}
'.' => {
fraction_empty = true;
state = State::OneDecimal;
}
ch if ch.is_ascii_digit() => {
parser.consume();
return Err(syntax_error(parser.loc(), "Invalid binary digit"));
}
_ => {
break;
}
},
State::Decimal => match ch {
'0' => {
exponent -= 1;
state = State::DigitDecimal;
fraction_empty = false;
}
'1' => {
mantissa = 1;
exponent -= 1;
state = State::OneDecimal;
fraction_empty = false;
}
ch if ch.is_ascii_digit() => {
parser.consume();
return Err(syntax_error(parser.loc(), "Invalid binary digit"));
}
_ => {
break;
}
},
State::DigitDecimal => match ch {
'0' => {
exponent -= 1;
fraction_empty = false;
}
'1' => {
mantissa = 1;
exponent -= 1;
state = State::OneDecimal;
fraction_empty = false;
}
ch if ch.is_ascii_digit() => {
parser.consume();
return Err(syntax_error(parser.loc(), "Invalid binary digit"));
}
_ => {
break;
}
},
State::OneDecimal => match ch {
'0' => {
if mantissa >= 0x8000_0000_0000_0000 {
parser.consume();
return Err(syntax_error(parser.loc(), "Too many significant digits"));
}
mantissa <<= 1;
}
'1' => {
if mantissa >= 0x8000_0000_0000_0000 {
parser.consume();
return Err(syntax_error(parser.loc(), "Too many significant digits"));
}
mantissa <<= 1;
mantissa |= 1;
}
ch if ch.is_ascii_digit() => {
parser.consume();
return Err(syntax_error(parser.loc(), "Invalid binary digit"));
}
_ => {
break;
}
},
}
parser.consume();
}
let ch = parser.peek();
if ch == 'e' || ch == 'p' || ch == 'P' || ch == 'E' {
parser.consume();
let loc = parser.loc();
let negexp = if parser.peek_and_consume('-') {
true
} else {
parser.peek_and_consume('+');
false
};
let digits = if settings.permit_underscores {
parser.take_while_unless(|ch| ch.is_ascii_digit(), |ch| ch == '_')
} else {
parser.take_while(|ch| ch.is_ascii_digit())
};
let power = match digits.parse::<i32>() {
Ok(value) => value,
Err(msg) => return Err(syntax_error(loc, &msg.to_string())),
};
if negexp {
exponent -= power;
} else {
exponent += power;
}
}
match state {
State::None | State::Decimal => {
return Err(syntax_error(loc, "Expected a number but did not find one"));
}
_ => {}
}
if whole_empty && !settings.permit_empty_whole {
return Err(syntax_error(
loc,
"An empty whole part is not permitted; there must be digits to the left of the decimal."
));
}
if fraction_empty && !settings.permit_empty_fraction {
return Err(syntax_error(
loc,
"An empty fraction part is not permitted; there must be digits to the right of the decimal, if present."
));
}
match state {
State::One | State::OneDecimal => {}
_ => return Ok(0.0),
}
if !(f64::MIN_EXP - 1..f64::MAX_EXP).contains(&exponent) {
return Err(syntax_error(parser.loc(), "Exponent is out of range"));
}
let mut bits = ((exponent + 1023) as u64) << 52;
let leading_zeros = mantissa.leading_zeros();
match leading_zeros.cmp(&11) {
std::cmp::Ordering::Less => {
mantissa >>= 11 - leading_zeros;
}
std::cmp::Ordering::Greater => {
mantissa <<= leading_zeros - 11;
}
_ => {}
}
mantissa &= 0x000f_ffff_ffff_ffff;
bits |= mantissa;
if negative {
bits |= 0x8000_0000_0000_0000;
}
Ok(f64::from_bits(bits))
}
#[cfg(test)]
mod test {
use crate::{
decoder::Decode,
numbers::{binary_float::parse_binary_float, NumberParserSettings},
ParserCore,
};
fn parse(value: &str) -> ParserCore {
let decoder = Decode::from_string(value);
ParserCore::new("<string>>", decoder)
}
#[test]
fn zero_test() {
let mut settings = NumberParserSettings::new();
settings.permit_underscores = false;
assert!(parse_binary_float(&mut parse(""), false, &settings).is_err());
assert_eq!(
parse_binary_float(&mut parse("0"), false, &settings).unwrap(),
0.0
);
assert_eq!(
parse_binary_float(&mut parse("0"), true, &settings).unwrap(),
0.0
);
}
#[test]
fn one_test() {
let mut settings = NumberParserSettings::new();
settings.permit_underscores = false;
assert_eq!(
parse_binary_float(&mut parse("1"), false, &settings).unwrap(),
1.0
);
assert_eq!(
parse_binary_float(&mut parse("1"), true, &settings).unwrap(),
-1.0
);
assert_eq!(
parse_binary_float(&mut parse("01"), false, &settings).unwrap(),
1.0
);
assert_eq!(
parse_binary_float(&mut parse("001"), true, &settings).unwrap(),
-1.0
);
}
#[test]
fn integer_test() {
let mut settings = NumberParserSettings::new();
settings.permit_underscores = false;
assert_eq!(
parse_binary_float(&mut parse("1111"), false, &settings).unwrap(),
15.0
);
assert_eq!(
parse_binary_float(&mut parse("1111"), true, &settings).unwrap(),
-15.0
);
settings.permit_underscores = true;
assert_eq!(
parse_binary_float(&mut parse("10_1001_1001"), false, &settings).unwrap(),
665.0
);
}
#[test]
fn fraction_test() {
let mut settings = NumberParserSettings::new();
settings.permit_underscores = false;
assert_eq!(
parse_binary_float(&mut parse(".1"), false, &settings).unwrap(),
0.5
);
assert_eq!(
parse_binary_float(&mut parse(".01"), false, &settings).unwrap(),
0.25
);
assert_eq!(
parse_binary_float(&mut parse("1.01"), false, &settings).unwrap(),
1.25
);
assert_eq!(
parse_binary_float(&mut parse("0.010"), false, &settings).unwrap(),
0.25
);
}
#[test]
fn reject_test_1() {
let mut settings = NumberParserSettings::new();
settings.permit_underscores = false;
assert!(parse_binary_float(&mut parse("2"), false, &settings).is_err());
assert!(parse_binary_float(&mut parse("k"), false, &settings).is_err());
assert!(parse_binary_float(&mut parse("02"), false, &settings).is_err());
assert!(parse_binary_float(&mut parse("12"), false, &settings).is_err());
}
#[test]
fn reject_test_2() {
let mut settings = NumberParserSettings::new();
settings.permit_underscores = false;
assert!(parse_binary_float(&mut parse("0.02"), false, &settings).is_err());
assert!(parse_binary_float(&mut parse("10.02"), false, &settings).is_err());
assert!(parse_binary_float(&mut parse(".2"), false, &settings).is_err());
assert!(parse_binary_float(&mut parse(".12"), false, &settings).is_err());
}
#[test]
fn reject_test_3() {
let mut settings = NumberParserSettings::new();
settings.permit_underscores = false;
assert!(parse_binary_float(&mut parse(".k"), false, &settings).is_err());
assert_eq!(
parse_binary_float(&mut parse("0.k"), false, &settings).unwrap(),
0.0
);
settings.permit_underscores = true;
assert!(parse_binary_float(
&mut parse("11111111_11111111_11111111_11111111_11111111_11111111_11111111_11111111_0"),
false,
&settings
)
.is_err());
assert!(parse_binary_float(
&mut parse("11111111_11111111_11111111_11111111_11111111_11111111_11111111_11111111_1"),
false,
&settings
)
.is_err());
}
#[test]
fn reject_test_4() {
let mut settings = NumberParserSettings::new();
settings.permit_underscores = true;
assert!(parse_binary_float(
&mut parse(
"1.1111111_11111111_11111111_11111111_11111111_11111111_11111111_11111111_0"
),
false,
&settings
)
.is_err());
assert!(parse_binary_float(
&mut parse(
"1.1111111_11111111_11111111_11111111_11111111_11111111_11111111_11111111_1"
),
false,
&settings
)
.is_err());
assert!(parse_binary_float(&mut parse("1e4294967296"), false, &settings).is_err());
}
#[test]
fn exponent_test() {
let mut settings = NumberParserSettings::new();
settings.permit_underscores = false;
assert_eq!(
parse_binary_float(&mut parse("0e1000"), false, &settings).unwrap(),
0.0
);
assert_eq!(
parse_binary_float(&mut parse("1e2"), false, &settings).unwrap(),
4.0
);
}
#[test]
fn limits_test() {
let mut settings = NumberParserSettings::new();
settings.permit_underscores = true;
assert_eq!(
parse_binary_float(
&mut parse(
"1.1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111e1023"
),
false,
&settings
)
.unwrap(),
f64::MAX
);
assert_eq!(
parse_binary_float(
&mut parse(
"1.1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111e1023"
),
true,
&settings
)
.unwrap(),
f64::MIN
);
assert_eq!(
parse_binary_float(&mut parse("1e-1022"), false, &settings).unwrap(),
f64::MIN_POSITIVE
);
}
#[test]
fn overflow_test() {
let mut settings = NumberParserSettings::new();
settings.permit_underscores = false;
assert!(parse_binary_float(&mut parse("1e1024"), false, &settings).is_err());
assert!(parse_binary_float(&mut parse("1e-1023"), false, &settings).is_err());
settings.permit_underscores = true;
assert_eq!(
parse_binary_float(
&mut parse(
"11111111_11111111_11111111_11111111_11111111_11111111_11111111_11111111"
),
false,
&settings
)
.unwrap(),
1.844674407370955e19f64
);
assert!(parse_binary_float(
&mut parse("11111111_11111111_11111111_11111111_11111111_11111111_11111111_11111111_1"),
false,
&settings
)
.is_err());
}
#[test]
fn empty_parts_test() {
let mut settings = NumberParserSettings::new();
settings.permit_empty_whole = false;
settings.permit_empty_fraction = false;
assert!(parse_binary_float(&mut parse("10.e1"), false, &settings).is_err());
assert!(parse_binary_float(&mut parse("10."), false, &settings).is_err());
assert!(parse_binary_float(&mut parse(".10e1"), false, &settings).is_err());
assert!(parse_binary_float(&mut parse(".10"), false, &settings).is_err());
}
}