use crate::{
errors::{syntax_error, ParseResult},
ParserCore,
};
use super::NumberParserSettings;
#[inline]
fn hex_digit_value(ch: char) -> u64 {
let dig = ch as u64;
if (0x30..=0x39).contains(&dig) {
dig - 0x30
} else if (0x41..0x5B).contains(&(dig & 0xdf)) {
(dig & 0xdf) - 0x37
} else {
255
}
}
#[inline]
fn exponent_adjust(value: u64) -> i32 {
64i32 - (value.leading_zeros() as i32)
}
pub fn parse_hexadecimal_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;
}
ch if ch.is_ascii_hexdigit() => {
mantissa = hex_digit_value(ch);
exponent = exponent_adjust(mantissa) - 1;
state = State::One;
}
'.' => {
whole_empty = true;
fraction_empty = true;
state = State::Decimal;
}
ch if ch.is_alphanumeric() => {
parser.consume();
return Err(syntax_error(
parser.loc(),
format!("Invalid hexadecimal digit '{}'", ch).as_str(),
));
}
_ => {
break;
}
},
State::Digit => match ch {
'0' => {}
ch if ch.is_ascii_hexdigit() => {
mantissa = hex_digit_value(ch);
exponent = exponent_adjust(mantissa) - 1;
state = State::One;
}
'.' => {
fraction_empty = true;
state = State::DigitDecimal;
}
'p' | 'P' => {
break;
}
ch if ch.is_alphanumeric() => {
parser.consume();
return Err(syntax_error(
parser.loc(),
format!("Invalid hexadecimal digit '{}'", ch).as_str(),
));
}
_ => {
break;
}
},
State::One => match ch {
'0' => {
if mantissa >= 0x1000_0000_0000_0000 {
parser.consume();
return Err(syntax_error(parser.loc(), "Too many significant digits"));
}
mantissa <<= 4;
exponent += 4;
}
ch if ch.is_ascii_hexdigit() => {
if mantissa >= 0x1000_0000_0000_0000 {
parser.consume();
return Err(syntax_error(parser.loc(), "Too many significant digits"));
}
mantissa <<= 4;
mantissa |= hex_digit_value(ch);
exponent += 4;
}
'.' => {
fraction_empty = true;
state = State::OneDecimal;
}
'p' | 'P' => {
break;
}
ch if ch.is_alphanumeric() => {
parser.consume();
return Err(syntax_error(
parser.loc(),
format!("Invalid hexadecimal digit '{}'", ch).as_str(),
));
}
_ => {
break;
}
},
State::Decimal => match ch {
'0' => {
exponent -= 4;
state = State::DigitDecimal;
fraction_empty = false;
}
ch if ch.is_ascii_hexdigit() => {
mantissa = hex_digit_value(ch);
exponent = exponent_adjust(mantissa) - 5;
state = State::OneDecimal;
fraction_empty = false;
}
'p' | 'P' => {
break;
}
ch if ch.is_alphanumeric() => {
parser.consume();
return Err(syntax_error(
parser.loc(),
format!("Invalid hexadecimal digit '{}'", ch).as_str(),
));
}
_ => {
break;
}
},
State::DigitDecimal => match ch {
'0' => {
exponent -= 4;
fraction_empty = false;
}
ch if ch.is_ascii_hexdigit() => {
mantissa = hex_digit_value(ch);
exponent += exponent_adjust(mantissa) - 5;
state = State::OneDecimal;
fraction_empty = false;
}
'p' | 'P' => {
break;
}
ch if ch.is_alphanumeric() => {
parser.consume();
return Err(syntax_error(
parser.loc(),
format!("Invalid hexadecimal digit '{}'", ch).as_str(),
));
}
_ => {
break;
}
},
State::OneDecimal => match ch {
'0' => {
if mantissa >= 0x1000_0000_0000_0000 {
parser.consume();
return Err(syntax_error(parser.loc(), "Too many significant digits"));
}
mantissa <<= 4;
}
ch if ch.is_ascii_hexdigit() => {
if mantissa >= 0x1000_0000_0000_0000 {
parser.consume();
return Err(syntax_error(parser.loc(), "Too many significant digits"));
}
mantissa <<= 4;
mantissa |= hex_digit_value(ch);
}
'p' | 'P' => {
break;
}
ch if ch.is_alphanumeric() => {
parser.consume();
return Err(syntax_error(
parser.loc(),
format!("Invalid hexadecimal digit '{}'", ch).as_str(),
));
}
_ => {
break;
}
},
}
parser.consume();
}
let ch = parser.peek();
if ch == 'p' || ch == 'P' {
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 * 4;
} else {
exponent += power * 4;
}
}
match state {
State::None | State::Decimal => {
return Err(syntax_error(
parser.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::{
hexadecimal_float::{hex_digit_value, parse_hexadecimal_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_hexadecimal_float(&mut parse(""), false, &settings).is_err());
assert_eq!(
parse_hexadecimal_float(&mut parse("0"), false, &settings).unwrap(),
0.0
);
assert_eq!(
parse_hexadecimal_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_hexadecimal_float(&mut parse("1"), false, &settings).unwrap(),
1.0
);
assert_eq!(
parse_hexadecimal_float(&mut parse("1"), true, &settings).unwrap(),
-1.0
);
assert_eq!(
parse_hexadecimal_float(&mut parse("01"), false, &settings).unwrap(),
1.0
);
assert_eq!(
parse_hexadecimal_float(&mut parse("001"), true, &settings).unwrap(),
-1.0
);
}
#[test]
fn integer_test_1() {
let mut settings = NumberParserSettings::new();
settings.permit_underscores = false;
assert_eq!(
parse_hexadecimal_float(&mut parse("f"), false, &settings).unwrap(),
15.0
);
assert_eq!(
parse_hexadecimal_float(&mut parse("8"), true, &settings).unwrap(),
-8.0
);
assert_eq!(
parse_hexadecimal_float(&mut parse("81e4"), true, &settings).unwrap(),
-33252.0
);
assert_eq!(
parse_hexadecimal_float(&mut parse("2120"), true, &settings).unwrap(),
-8480.0
);
}
#[test]
fn integer_test_2() {
let mut settings = NumberParserSettings::new();
settings.permit_underscores = false;
assert_eq!(
parse_hexadecimal_float(&mut parse("6f"), false, &settings).unwrap(),
111.0
);
settings.permit_underscores = true;
assert_eq!(
parse_hexadecimal_float(&mut parse("48_e2"), false, &settings).unwrap(),
18658.0
);
assert_eq!(
parse_hexadecimal_float(&mut parse("197ffe664cb333"), true, &settings).unwrap(),
-7177605032489779.0
);
assert_eq!(
parse_hexadecimal_float(&mut parse("2_9_9"), false, &settings).unwrap(),
665.0
);
}
#[test]
fn fraction_test_1() {
let mut settings = NumberParserSettings::new();
settings.permit_underscores = false;
assert_eq!(
parse_hexadecimal_float(&mut parse("0.1"), false, &settings).unwrap(),
1.0 / 16.0
);
assert_eq!(
parse_hexadecimal_float(&mut parse("0.02"), false, &settings).unwrap(),
2.0 / 16.0 / 16.0
);
assert_eq!(
parse_hexadecimal_float(&mut parse("0.8"), false, &settings).unwrap(),
0.5
);
assert_eq!(
parse_hexadecimal_float(&mut parse("1.8"), false, &settings).unwrap(),
1.5
);
}
#[test]
fn fraction_test_2() {
let mut settings = NumberParserSettings::new();
settings.permit_underscores = false;
assert_eq!(
parse_hexadecimal_float(&mut parse("0.08"), false, &settings).unwrap(),
0.03125
);
assert_eq!(
parse_hexadecimal_float(&mut parse("10.008"), false, &settings).unwrap(),
16.001953125
);
assert_eq!(
parse_hexadecimal_float(&mut parse("e.e"), false, &settings).unwrap(),
14.875
);
assert_eq!(
parse_hexadecimal_float(&mut parse("000.0000"), false, &settings).unwrap(),
0.0
);
}
#[test]
fn fraction_test_3() {
let mut settings = NumberParserSettings::new();
settings.permit_underscores = false;
assert_eq!(
parse_hexadecimal_float(&mut parse("0.8,"), false, &settings).unwrap(),
0.5
);
assert_eq!(
parse_hexadecimal_float(&mut parse("1.8,"), false, &settings).unwrap(),
1.5
);
assert_eq!(
parse_hexadecimal_float(&mut parse(".08,"), false, &settings).unwrap(),
1.0 / 32.0
);
}
#[test]
fn exponent_test_1() {
let mut settings = NumberParserSettings::new();
settings.permit_underscores = false;
assert_eq!(
parse_hexadecimal_float(&mut parse("0p1000"), false, &settings).unwrap(),
0.0
);
assert_eq!(
parse_hexadecimal_float(&mut parse("10p1"), false, &settings).unwrap(),
256.0
);
assert_eq!(
parse_hexadecimal_float(&mut parse("10p-1"), false, &settings).unwrap(),
1.0
);
assert_eq!(
parse_hexadecimal_float(&mut parse("0.001p3"), false, &settings).unwrap(),
1.0
);
}
#[test]
fn exponent_test_2() {
let mut settings = NumberParserSettings::new();
settings.permit_underscores = false;
assert_eq!(
parse_hexadecimal_float(&mut parse("1021p-3"), false, &settings).unwrap(),
1.008056640625
);
assert_eq!(
parse_hexadecimal_float(&mut parse("44p-3"), false, &settings).unwrap(),
0.0166015625
);
assert_eq!(
parse_hexadecimal_float(&mut parse("0.1p0,"), false, &settings).unwrap(),
1.0 / 16.0
);
assert_eq!(
parse_hexadecimal_float(&mut parse("1.p0,"), false, &settings).unwrap(),
1.0
);
}
#[test]
fn exponent_test_3() {
let mut settings = NumberParserSettings::new();
settings.permit_underscores = false;
assert_eq!(
parse_hexadecimal_float(&mut parse("0.p12"), false, &settings).unwrap(),
0.0
);
assert_eq!(
parse_hexadecimal_float(&mut parse(".0p12"), false, &settings).unwrap(),
0.0
);
}
#[test]
fn limits_test() {
let mut settings = NumberParserSettings::new();
settings.permit_underscores = true;
assert_eq!(
parse_hexadecimal_float(&mut parse("f.f_ffff_ffff_ffffp255"), false, &settings)
.unwrap(),
f64::MAX
);
assert_eq!(
parse_hexadecimal_float(&mut parse("f.f_ffff_ffff_ffffp255"), true, &settings).unwrap(),
f64::MIN
);
assert_eq!(
parse_hexadecimal_float(&mut parse("0.4p-255"), false, &settings).unwrap(),
f64::MIN_POSITIVE
);
}
#[test]
fn overflow_test() {
let mut settings = NumberParserSettings::new();
settings.permit_underscores = false;
assert!(parse_hexadecimal_float(&mut parse("1p256"), false, &settings).is_err());
assert!(parse_hexadecimal_float(&mut parse("2p-256"), false, &settings).is_err());
settings.permit_underscores = true;
assert_eq!(
parse_hexadecimal_float(&mut parse("ff_ff_ff_ff_ff_ff_ff_ff"), false, &settings)
.unwrap(),
1.844674407370955e19f64
);
assert!(
parse_hexadecimal_float(&mut parse("ff_ff_ff_ff_ff_ff_ff_ff_1"), false, &settings)
.is_err()
);
}
#[test]
fn digit_test() {
for ch in "0123456789abcdefABCDEF".chars() {
assert_eq!(
hex_digit_value(ch),
u64::from_str_radix(&ch.to_string(), 16).unwrap()
);
}
assert_eq!(hex_digit_value('*'), 255_u64);
}
#[test]
fn reject_test_1() {
let mut settings = NumberParserSettings::new();
settings.permit_underscores = false;
assert!(parse_hexadecimal_float(&mut parse("g"), false, &settings).is_err());
assert!(parse_hexadecimal_float(&mut parse(","), false, &settings).is_err());
}
#[test]
fn reject_test_2() {
let mut settings = NumberParserSettings::new();
settings.permit_underscores = false;
assert!(parse_hexadecimal_float(&mut parse("0g"), false, &settings).is_err());
assert_eq!(
parse_hexadecimal_float(&mut parse("0,"), false, &settings).unwrap(),
0.0
);
assert!(parse_hexadecimal_float(&mut parse("1g"), false, &settings).is_err());
assert_eq!(
parse_hexadecimal_float(&mut parse("1,"), false, &settings).unwrap(),
1.0
);
}
#[test]
fn reject_test_3() {
let mut settings = NumberParserSettings::new();
settings.permit_underscores = false;
assert!(parse_hexadecimal_float(&mut parse("0.g"), false, &settings).is_err());
assert_eq!(
parse_hexadecimal_float(&mut parse("0.,"), false, &settings).unwrap(),
0.0
);
assert!(parse_hexadecimal_float(&mut parse("1.g"), false, &settings).is_err());
assert_eq!(
parse_hexadecimal_float(&mut parse("1.,"), false, &settings).unwrap(),
1.0
);
}
#[test]
fn reject_test_4() {
let mut settings = NumberParserSettings::new();
settings.permit_underscores = false;
assert!(parse_hexadecimal_float(&mut parse("0.0g"), false, &settings).is_err());
assert!(parse_hexadecimal_float(&mut parse("1.0g"), false, &settings).is_err());
}
#[test]
fn reject_test_5() {
let mut settings = NumberParserSettings::new();
settings.permit_underscores = false;
assert!(parse_hexadecimal_float(&mut parse(".g"), false, &settings).is_err());
assert!(parse_hexadecimal_float(&mut parse(".,"), false, &settings).is_err());
}
#[test]
fn reject_test_6() {
let mut settings = NumberParserSettings::new();
settings.permit_underscores = true;
assert!(parse_hexadecimal_float(
&mut parse("0.1_fffff_ffff_ffff_ffff_ffff"),
false,
&settings
)
.is_err());
assert!(parse_hexadecimal_float(
&mut parse("1.1_fffff_ffff_ffff_ffff_ffff"),
false,
&settings
)
.is_err());
assert!(parse_hexadecimal_float(
&mut parse("0.1_fffff_ffff_ffff_ffff_fff0"),
false,
&settings
)
.is_err());
assert!(parse_hexadecimal_float(
&mut parse("1.1_fffff_ffff_ffff_ffff_fff0"),
false,
&settings
)
.is_err());
}
#[test]
fn reject_test_7() {
let mut settings = NumberParserSettings::new();
settings.permit_underscores = true;
assert!(
parse_hexadecimal_float(&mut parse("11000_0000_0000_0000"), false, &settings).is_err()
);
assert!(
parse_hexadecimal_float(&mut parse("1.1000_0000_0000_0000"), 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_hexadecimal_float(&mut parse("10.p1"), false, &settings).is_err());
assert!(parse_hexadecimal_float(&mut parse("10."), false, &settings).is_err());
assert!(parse_hexadecimal_float(&mut parse(".10p1"), false, &settings).is_err());
assert!(parse_hexadecimal_float(&mut parse(".10"), false, &settings).is_err());
}
}