From 9169bdd904486327772f3547b7578ec6135e46bd Mon Sep 17 00:00:00 2001 From: Sebastian Turner Date: Sat, 17 Oct 2026 18:10:02 -0700 Subject: [PATCH] Fix parse_f64_approx returning NaN for 0e400 Signed-off-by: Sebastian Turner --- crates/edit/src/float.rs | 23 +++++++++++++++++++++-- crates/edit/src/json.rs | 1 + 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/crates/edit/src/float.rs b/crates/edit/src/float.rs index 52a4de2246f..43278c92d14 100644 --- a/crates/edit/src/float.rs +++ b/crates/edit/src/float.rs @@ -119,9 +119,12 @@ pub fn parse_f64_approx(input: &[u8]) -> Option { let mut value = mantissa as f64; - if exponent != 0 { + // A zero mantissa stays zero. Scaling it by 10^exponent would turn + // e.g. "0e400" into 0 * inf = NaN. + if exponent != 0 && mantissa != 0 { const TABLE: [f64; 8] = [1e-3, 1e-2, 1e-1, 1e0, 1e1, 1e2, 1e3, 1e4]; - value *= match TABLE.get((exponent + 3) as usize) { + // The exponent saturates at i32::MAX/MIN above, so the offset must not overflow. + value *= match TABLE.get(exponent.wrapping_add(3) as usize) { Some(&v) => v, None => 10f64.powi(exponent), }; @@ -211,6 +214,22 @@ mod tests { assert!((v - 12345678901234567890.0f64).abs() / v < 1e-15); } + // ---- Edge: zero with a large exponent ---- + #[test] + fn zero_with_large_exponent() { + assert_eq!(p("0e400"), 0.0); + assert_eq!(p("0.0e999"), 0.0); + assert!(p("-0e400").is_sign_negative()); + } + + // ---- Edge: exponent close to i32::MAX ---- + #[test] + fn huge_exponent() { + assert_eq!(p("1e2147483645"), f64::INFINITY); + assert_eq!(p("1e99999999999"), f64::INFINITY); + assert_eq!(p("1e-99999999999"), 0.0); + } + // ---- Errors ---- #[test] fn errors() { diff --git a/crates/edit/src/json.rs b/crates/edit/src/json.rs index ccea0870739..bc632c6e773 100644 --- a/crates/edit/src/json.rs +++ b/crates/edit/src/json.rs @@ -623,6 +623,7 @@ mod tests { assert_eq!(parse(&scratch, "3.15").unwrap().as_number(), Some(3.15)); assert_eq!(parse(&scratch, "1e10").unwrap().as_number(), Some(1e10)); assert_eq!(parse(&scratch, "1.5e-3").unwrap().as_number(), Some(0.0015)); + assert_eq!(parse(&scratch, "0e400").unwrap().as_number(), Some(0.0)); } #[test]