diff --git a/Cargo.toml b/Cargo.toml index de139d9..7e40962 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ big-endian-varlen=[] lazy_static = "1.4" smallvec = "1.3" cfg-if = "0.1" +strtod = "0.0.1" [dev-dependencies] criterion = "^0.3" diff --git a/benches/benchmark.rs b/benches/benchmark.rs index 1817d99..1c48151 100644 --- a/benches/benchmark.rs +++ b/benches/benchmark.rs @@ -109,7 +109,7 @@ fn into_benchmark(c: &mut Criterion) { c.bench_function("to_f64", |b| { let val = parse("1.234567890123456789e10"); b.iter(|| { - let _n: f32 = into(black_box(&val)); + let _n: f64 = into(black_box(&val)); }) }); } diff --git a/src/convert.rs b/src/convert.rs index 777ecf6..91b13c1 100644 --- a/src/convert.rs +++ b/src/convert.rs @@ -625,11 +625,23 @@ impl<'a> TryFrom<&NumericVar<'a>> for f32 { #[inline] fn try_from(value: &NumericVar<'a>) -> Result { use std::fmt::Write; + if value.is_nan() { + return Ok(std::f32::NAN); + } let mut buf = String::with_capacity(32); buf.write_fmt(format_args!("{}", value)) .expect("returned an error unexpectedly"); - let f = buf.parse::()?; - Ok(f) + + match strtod::strtod(&buf) { + Some(val) => { + let f = val as f32; + if (f.is_infinite() && !val.is_infinite()) || (f == 0.0 && val != 0.0) { + return Err(NumericTryFromError::overflow()); + } + Ok(f) + } + None => Err(NumericTryFromError::overflow()), + } } } @@ -639,11 +651,17 @@ impl<'a> TryFrom<&NumericVar<'a>> for f64 { #[inline] fn try_from(value: &NumericVar<'a>) -> Result { use std::fmt::Write; + if value.is_nan() { + return Ok(std::f64::NAN); + } let mut buf = String::with_capacity(32); buf.write_fmt(format_args!("{}", value)) .expect("returned an error unexpectedly"); - let f = buf.parse::()?; - Ok(f) + + match strtod::strtod(&buf) { + Some(f) => Ok(f), + None => Err(NumericTryFromError::overflow()), + } } } @@ -1140,7 +1158,7 @@ mod tests { assert_try_into("1.23456789e-10", 1.23456789e-10f32); assert_try_into("3.40282347e+38", std::f32::MAX); assert_try_into("-3.40282347e+38", std::f32::MIN); - assert_try_into("1e39", std::f32::INFINITY); + assert_try_into_overflow::("1e39"); assert_try_into("1.17549435e-38", std::f32::MIN_POSITIVE); assert!(try_into::<&str, f32>("NaN").is_nan()); } diff --git a/src/var.rs b/src/var.rs index 428a068..2aab4b1 100644 --- a/src/var.rs +++ b/src/var.rs @@ -2102,17 +2102,17 @@ impl<'a> NumericVar<'a> { /// `self` is displayed to the number of digits indicated by its dscale. pub fn write(&self, f: &mut W) -> Result<(), fmt::Error> { if self.is_nan() { - return write!(f, "NaN"); + return f.write_str("NaN"); } // Output a dash for negative values. if self.sign == NUMERIC_NEG { - write!(f, "-")?; + f.write_char('-')?; } // Output all digits before the decimal point. if self.weight < 0 { - write!(f, "0")?; + f.write_char('0')?; } else { let digits = self.digits(); debug_assert_eq!(digits.len(), self.ndigits as usize); @@ -2137,7 +2137,7 @@ impl<'a> NumericVar<'a> { // If requested, output a decimal point and all the digits that follow it. if self.dscale > 0 { - write!(f, ".")?; + f.write_char('.')?; let digits = self.digits(); debug_assert_eq!(digits.len(), self.ndigits as usize);