pub fn limited_float_parse(digits: &str) -> Option<f32> {
let (digits, sign) = if let Some(digits) = digits.strip_prefix('-') {
(digits, -1.0f64)
} else {
(digits, 1.0f64)
};
let digits_bytes = digits.as_bytes();
let integer_len = digits_bytes
.iter()
.position(|&b| b == b'.')
.unwrap_or(digits.len());
let (integer, fraction_with_dot) = digits_bytes.split_at_checked(integer_len)?;
let fraction = fraction_with_dot.get(1..).unwrap_or(&[]);
let mut buffer = [0u8; 20];
let mut remaining_buffer = buffer.as_mut_slice();
let integer_buffer = remaining_buffer.split_off_mut(..integer.len())?;
integer_buffer.copy_from_slice(integer);
let fraction_buffer = remaining_buffer.split_off_mut(..fraction.len())?;
fraction_buffer.copy_from_slice(fraction);
let num_without_dot = &buffer[..(integer.len() + fraction.len())];
debug_assert!(std::str::from_utf8(num_without_dot).is_ok());
let num_without_dot = unsafe { std::str::from_utf8_unchecked(num_without_dot) };
let mut value = num_without_dot.parse::<u64>().ok()? as f64;
value /= 10f64.powi(fraction.len() as i32);
value *= sign;
Some(value as f32)
}
#[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.18446743
);
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);
}
}