pub fn limited_float_parse(digits: &str) -> Option<f32> {
let mut bytes = digits.as_bytes();
let neg = match bytes.strip_prefix(b"-") {
Some(rest) => {
bytes = rest;
true
}
None => false,
};
let mut mantissa: u64 = 0;
let mut divisor: f64 = 1.0;
let mut seen_dot = false;
let mut seen_digit = false;
for &b in bytes {
if b == b'.' {
if seen_dot {
return None; }
seen_dot = true;
} else {
let d = b.wrapping_sub(b'0');
if d > 9 {
return None; }
mantissa = mantissa.checked_mul(10)?.checked_add(d as u64)?;
if seen_dot {
divisor *= 10.0;
}
seen_digit = true;
}
}
if !seen_digit {
return None; }
let v = (mantissa as f64 / divisor) as f32;
Some(if neg { -v } else { v })
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn invalid_floats() {
assert!(limited_float_parse("1..0").is_none());
assert!(limited_float_parse("--1").is_none());
assert!(limited_float_parse("hello").is_none());
assert!(limited_float_parse("").is_none());
assert!(limited_float_parse(" 1.0").is_none());
assert!(limited_float_parse("1.0 ").is_none());
assert!(limited_float_parse(" 1.0 ").is_none());
assert!(limited_float_parse("18446744073709551616").is_none());
assert!(limited_float_parse("100000000000000.000000").is_none());
assert!(limited_float_parse("10👍🏽.0").is_none());
}
#[test]
fn test_simple_float_parse() {
assert_eq!(limited_float_parse("1.0").unwrap(), 1.0);
assert_eq!(limited_float_parse("0001.0000").unwrap(), 1.0);
assert_eq!(
limited_float_parse("18446744073709551615").unwrap(),
1.8446744e19
);
assert_eq!(
limited_float_parse("-18446744073.709551615").unwrap(),
-18446744000.0
);
assert_eq!(
limited_float_parse("0.1844674407370955161").unwrap(),
"0.1844674407370955161".parse::<f32>().unwrap()
);
assert_eq!(limited_float_parse("0.0000000000000000001").unwrap(), 1e-19);
assert_eq!(limited_float_parse("16777216.0").unwrap(), 16777216.0);
assert_eq!(limited_float_parse("16777217.0").unwrap(), 16777216.0);
assert_eq!(limited_float_parse("16777218.0").unwrap(), 16777218.0);
assert_eq!(limited_float_parse("16777219.0").unwrap(), 16777220.0);
}
}