use std::{error::Error, fmt::Display};
#[rustfmt::skip]
const POW_10_TABLE: [f64; 23] = [
1e0 , 1e1 , 1e2 , 1e3 , 1e4 , 1e5 , 1e6 , 1e7 , 1e8 , 1e9 ,
1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
1e20, 1e21, 1e22,
];
pub fn parse_endf_float<F: AsRef<[u8]>>(float: F) -> Result<f64, ParseEndfFloatError> {
let float = float.as_ref();
if float.is_empty() {
return Err(ParseEndfFloatError);
}
if float.len() > 11 {
return Err(ParseEndfFloatError);
}
let mut iter = float.iter().filter(|&b| *b != b' ').peekable();
let negative = match iter.peek() {
None => return Ok(0.),
Some(b'-') => {
iter.next();
true
}
Some(b'+') => {
iter.next();
false
}
Some(_) => false,
};
if iter.peek().is_none() {
return Ok(0.);
}
let mut mantissa = 0;
let mut exponent = 0;
loop {
match iter.peek() {
Some(&byte) if byte.is_ascii_digit() => {
mantissa = mantissa * 10 + (byte - b'0') as i64; iter.next();
}
_ => break,
}
}
if iter.peek() == Some(&&b'.') {
iter.next();
loop {
match iter.peek() {
Some(&byte) if byte.is_ascii_digit() => {
mantissa = mantissa * 10 + (byte - b'0') as i64; exponent -= 1; iter.next();
}
_ => break,
}
}
}
let mut exp_sep = false;
match iter.peek() {
Some(b'e') | Some(b'E') | Some(b'd') | Some(b'D') => {
exp_sep = true;
iter.next();
}
_ => {}
}
let negative_exponent = match iter.peek() {
Some(b'-') => {
exp_sep = true;
iter.next();
true
}
Some(b'+') => {
exp_sep = true;
iter.next();
false
}
_ => false,
};
if exp_sep && iter.peek().is_none() {
return Err(ParseEndfFloatError);
}
let mut exp = 0;
loop {
match iter.peek() {
Some(&byte) if byte.is_ascii_digit() => {
exp = exp * 10 + (byte - b'0') as i32; iter.next();
}
_ => break,
}
}
if iter.peek().is_some() {
return Err(ParseEndfFloatError);
}
if mantissa == 0 {
return Ok(0.);
}
if negative_exponent {
exponent -= exp;
} else {
exponent += exp;
}
let mut value = if exponent.abs() > 22 {
let float = format!("{mantissa}e{exponent}");
match float.parse() {
Ok(value) => value,
Err(_) => return Err(ParseEndfFloatError),
}
} else {
let mut value = mantissa as f64;
if exponent < 0 {
value /= POW_10_TABLE[-exponent as usize]
} else {
value *= POW_10_TABLE[exponent as usize]
}
value
};
if negative {
value = -value;
}
Ok(value)
}
#[derive(Debug)]
pub struct ParseEndfFloatError;
impl Display for ParseEndfFloatError {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(fmt, "parse ENDF float error")
}
}
impl Error for ParseEndfFloatError {}
#[cfg(test)]
mod tests {
use super::*;
fn assert_endf_float_eq(str: &str, value: f64) {
let float = parse_endf_float(str).unwrap_or_else(|_| panic!("error parsing \"{str}\""));
assert_eq!(float, value);
}
#[test]
fn empty_slice() {
assert!(parse_endf_float("").is_err());
}
#[test]
fn too_long() {
assert!(parse_endf_float(" 1.234567890").is_err());
assert!(parse_endf_float("-1.23456E-12").is_err());
}
#[test]
fn exponential_separator_only() {
assert!(parse_endf_float("e").is_err());
assert!(parse_endf_float("E").is_err());
assert!(parse_endf_float("d").is_err());
assert!(parse_endf_float("D").is_err());
}
#[test]
fn invalid_sign() {
assert!(parse_endf_float("*1.23").is_err());
}
#[test]
fn invalid_digit() {
assert!(parse_endf_float("a.23").is_err());
assert!(parse_endf_float("1.2a").is_err());
assert!(parse_endf_float("1.23e-a").is_err());
}
#[test]
fn invalid_integral_fractional_separator() {
assert!(parse_endf_float("1,23").is_err());
}
#[test]
fn invalid_exponential_separator() {
assert!(parse_endf_float("1.23f4").is_err());
}
#[test]
fn empty_exponential_part() {
assert!(parse_endf_float("1.2E").is_err());
assert!(parse_endf_float("1.2-").is_err());
assert!(parse_endf_float("1.2+").is_err());
}
#[test]
fn blank() {
assert_endf_float_eq(" ", 0.);
assert_endf_float_eq(" ", 0.);
assert_endf_float_eq(" ", 0.);
assert_endf_float_eq(" ", 0.);
assert_endf_float_eq(" ", 0.);
assert_endf_float_eq(" ", 0.);
assert_endf_float_eq(" ", 0.);
assert_endf_float_eq(" ", 0.);
assert_endf_float_eq(" ", 0.);
assert_endf_float_eq(" ", 0.);
assert_endf_float_eq(" ", 0.);
}
#[test]
fn standard() {
assert_endf_float_eq(" 0.0", 0.);
assert_endf_float_eq(" +0.0", 0.);
assert_endf_float_eq(" -0.0", -0.);
assert_endf_float_eq(" 1.0", 1.);
assert_endf_float_eq(" +1.0", 1.);
assert_endf_float_eq(" -1.0", -1.);
}
#[test]
fn space_padding() {
assert_endf_float_eq("1.0 ", 1.);
assert_endf_float_eq(" 1.0 ", 1.);
assert_endf_float_eq(" 1.0", 1.);
}
#[test]
fn zero_padding() {
assert_endf_float_eq(" 1.00000000", 1.);
assert_endf_float_eq(" 0001.00000", 1.);
assert_endf_float_eq(" 00000001.0", 1.);
}
#[test]
fn decimal_separator_only() {
assert_endf_float_eq(" .", 0.);
}
#[test]
fn sign_only() {
assert_endf_float_eq(" +", 0.);
assert_endf_float_eq(" -", 0.);
}
#[test]
fn sign_decimal_separator_only() {
assert_endf_float_eq(" +.", 0.);
assert_endf_float_eq(" -.", -0.);
}
#[test]
fn exponent_padding() {
assert_endf_float_eq(" 1E0000000", 1.);
assert_endf_float_eq(" 1E+0000000", 1.);
assert_endf_float_eq(" 1E-0000000", 1.);
}
#[test]
fn exponent_only() {
assert_endf_float_eq("E1", 0.);
assert_endf_float_eq("E12", 0.);
assert_endf_float_eq("E123", 0.);
assert_endf_float_eq("E+1", 0.);
assert_endf_float_eq("E+12", 0.);
assert_endf_float_eq("E+123", 0.);
assert_endf_float_eq("E-1", 0.);
assert_endf_float_eq("E-12", 0.);
assert_endf_float_eq("E-123", 0.);
assert_endf_float_eq("E1", 0.);
assert_endf_float_eq("+.E1", 0.);
assert_endf_float_eq("-.E1", 0.);
}
#[test]
fn empty_integral_part() {
assert_endf_float_eq(".1e-2", 0.1e-2);
}
#[test]
fn empty_fractional_part() {
assert_endf_float_eq("1.e-2", 1.0e-2);
}
#[test]
fn decimal_separator_end() {
assert_endf_float_eq(" 0.", 0.);
assert_endf_float_eq("-1.", -1.);
assert_endf_float_eq("+1.", 1.);
}
#[test]
fn space_ignored() {
assert_endf_float_eq("1 . 2", 1.2);
assert_endf_float_eq("1 . 2 E - 3", 1.2e-3);
assert_endf_float_eq("1 E 2", 1.0e2);
assert_endf_float_eq("1. E -2", 1.0e-2);
assert_endf_float_eq("- 1 . E + 2", -1.0e2);
assert_endf_float_eq("+ 1 . E - 2", 1.0e-2);
assert_endf_float_eq(" - 1 . 2 ", -1.2);
assert_endf_float_eq("1 2 3 4 . 5", 1.2345e3);
assert_endf_float_eq("1 2 . 3 4 5", 1.2345e1);
assert_endf_float_eq("1 . 2 e 1 2", 1.2e12);
}
#[test]
fn integers() {
assert_endf_float_eq(" 1", 1.);
assert_endf_float_eq(" 12", 12.);
assert_endf_float_eq(" 123", 123.);
assert_endf_float_eq(" 1234", 1234.);
assert_endf_float_eq(" 12345", 12345.);
assert_endf_float_eq(" 123456", 123456.);
assert_endf_float_eq(" 1234567", 1234567.);
assert_endf_float_eq(" 12345678", 12345678.);
assert_endf_float_eq(" 123456789", 123456789.);
assert_endf_float_eq(" 1234567890", 1234567890.);
assert_endf_float_eq("-1", -1.);
assert_endf_float_eq("-12", -12.);
assert_endf_float_eq("-123", -123.);
assert_endf_float_eq("-1234", -1234.);
assert_endf_float_eq("-12345", -12345.);
assert_endf_float_eq("-123456", -123456.);
assert_endf_float_eq("-1234567", -1234567.);
assert_endf_float_eq("-12345678", -12345678.);
assert_endf_float_eq("-123456789", -123456789.);
assert_endf_float_eq("-1234567890", -1234567890.);
assert_endf_float_eq("+1", 1.);
assert_endf_float_eq("+12", 12.);
assert_endf_float_eq("+123", 123.);
assert_endf_float_eq("+1234", 1234.);
assert_endf_float_eq("+12345", 12345.);
assert_endf_float_eq("+123456", 123456.);
assert_endf_float_eq("+1234567", 1234567.);
assert_endf_float_eq("+12345678", 12345678.);
assert_endf_float_eq("+123456789", 123456789.);
assert_endf_float_eq("+1234567890", 1234567890.);
}
#[test]
fn floats() {
assert_endf_float_eq(" 1.", 1.);
assert_endf_float_eq(" 1.0", 1.);
assert_endf_float_eq(" 1.2", 1.2);
assert_endf_float_eq(" 1.23", 1.23);
assert_endf_float_eq(" 1.234", 1.234);
assert_endf_float_eq(" 1.2345", 1.2345);
assert_endf_float_eq(" 1.23456", 1.23456);
assert_endf_float_eq(" 1.234567", 1.234567);
assert_endf_float_eq(" 1.2345678", 1.2345678);
assert_endf_float_eq(" 1.23456789", 1.23456789);
assert_endf_float_eq("-1.", -1.);
assert_endf_float_eq("-1.0", -1.);
assert_endf_float_eq("-1.2", -1.2);
assert_endf_float_eq("-1.23", -1.23);
assert_endf_float_eq("-1.234", -1.234);
assert_endf_float_eq("-1.2345", -1.2345);
assert_endf_float_eq("-1.23456", -1.23456);
assert_endf_float_eq("-1.234567", -1.234567);
assert_endf_float_eq("-1.2345678", -1.2345678);
assert_endf_float_eq("-1.23456789", -1.23456789);
assert_endf_float_eq("+1.", 1.);
assert_endf_float_eq("+1.0", 1.);
assert_endf_float_eq("+1.2", 1.2);
assert_endf_float_eq("+1.23", 1.23);
assert_endf_float_eq("+1.234", 1.234);
assert_endf_float_eq("+1.2345", 1.2345);
assert_endf_float_eq("+1.23456", 1.23456);
assert_endf_float_eq("+1.234567", 1.234567);
assert_endf_float_eq("+1.2345678", 1.2345678);
assert_endf_float_eq("+1.23456789", 1.23456789);
}
#[test]
fn scientific_one_digit() {
assert_endf_float_eq(" 1.0E+1", 1.0e+1);
assert_endf_float_eq(" 1.2E+1", 1.2e+1);
assert_endf_float_eq(" 1.23E+1", 1.23e+1);
assert_endf_float_eq(" 1.234E+1", 1.234e+1);
assert_endf_float_eq(" 1.2345E+1", 1.2345e+1);
assert_endf_float_eq(" 1.23456E+1", 1.23456e+1);
assert_endf_float_eq(" 1.0+1", 1.0e+1);
assert_endf_float_eq(" 1.2+1", 1.2e+1);
assert_endf_float_eq(" 1.23+1", 1.23e+1);
assert_endf_float_eq(" 1.234+1", 1.234e+1);
assert_endf_float_eq(" 1.2345+1", 1.2345e+1);
assert_endf_float_eq(" 1.23456+1", 1.23456e+1);
assert_endf_float_eq(" 1.234567+1", 1.234567e+1);
assert_endf_float_eq(" 1.0e+1", 1.0e+1);
assert_endf_float_eq(" 1.0D+1", 1.0e+1);
assert_endf_float_eq(" 1.0d+1", 1.0e+1);
}
#[test]
fn scientific_two_digits() {
assert_endf_float_eq(" 1.0E+01", 1.0e+1);
assert_endf_float_eq(" 1.2E+01", 1.2e+1);
assert_endf_float_eq(" 1.23E+01", 1.23e+1);
assert_endf_float_eq(" 1.234E+01", 1.234e+1);
assert_endf_float_eq(" 1.2345E+01", 1.2345e+1);
assert_endf_float_eq(" 1.0+01", 1.0e+1);
assert_endf_float_eq(" 1.2+01", 1.2e+1);
assert_endf_float_eq(" 1.23+01", 1.23e+1);
assert_endf_float_eq(" 1.234+01", 1.234e+1);
assert_endf_float_eq(" 1.2345+01", 1.2345e+1);
assert_endf_float_eq(" 1.23456+01", 1.23456e+1);
assert_endf_float_eq(" 1.0e+01", 1.0e+1);
assert_endf_float_eq(" 1.0D+01", 1.0e+1);
assert_endf_float_eq(" 1.0d+01", 1.0e+1);
}
#[test]
fn scientific_three_digits() {
assert_endf_float_eq(" 1.0E+001", 1.0e+1);
assert_endf_float_eq(" 1.2E+001", 1.2e+1);
assert_endf_float_eq(" 1.23E+001", 1.23e+1);
assert_endf_float_eq(" 1.234E+001", 1.234e+1);
assert_endf_float_eq(" 1.0+001", 1.0e+1);
assert_endf_float_eq(" 1.2+001", 1.2e+1);
assert_endf_float_eq(" 1.23+001", 1.23e+1);
assert_endf_float_eq(" 1.234+001", 1.234e+1);
assert_endf_float_eq(" 1.2345+001", 1.2345e+1);
assert_endf_float_eq(" 1.0e+001", 1.0e+1);
assert_endf_float_eq(" 1.0D+001", 1.0e+1);
assert_endf_float_eq(" 1.0d+001", 1.0e+1);
}
#[test]
fn large_exponent() {
assert_endf_float_eq("1.234567E23", 1.234567e23);
assert_endf_float_eq("1.23456E123", 1.23456e123);
assert_endf_float_eq("-1.23456E23", -1.23456e23);
assert_endf_float_eq("-1.2345E123", -1.2345e123);
assert_endf_float_eq("1.23456E-23", 1.23456e-23);
assert_endf_float_eq("1.2345E-123", 1.2345e-123);
assert_endf_float_eq("-1.2345E-23", -1.2345e-23);
assert_endf_float_eq("-1.234E-123", -1.234e-123);
}
}