core: Shuffle around float parsing
Stop using stability to hide the implementation details of ParseFloatError and instead move the error type into the `dec2flt` module. Also move the implementation blocks of `FromStr for f{32,64}` into `dec2flt` directly.
This commit is contained in:
parent
5990249e48
commit
a2b932c0b6
9 changed files with 133 additions and 138 deletions
|
@ -96,8 +96,9 @@
|
|||
issue = "0")]
|
||||
|
||||
use prelude::v1::*;
|
||||
use num::ParseFloatError as PFE;
|
||||
use num::FloatErrorKind;
|
||||
use fmt;
|
||||
use str::FromStr;
|
||||
|
||||
use self::parse::{parse_decimal, Decimal, Sign};
|
||||
use self::parse::ParseResult::{self, Valid, ShortcutToInf, ShortcutToZero};
|
||||
use self::num::digits_to_big;
|
||||
|
@ -110,14 +111,87 @@ mod num;
|
|||
pub mod rawfp;
|
||||
pub mod parse;
|
||||
|
||||
/// Entry point for decimal-to-f32 conversion.
|
||||
pub fn to_f32(s: &str) -> Result<f32, PFE> {
|
||||
dec2flt(s)
|
||||
macro_rules! from_str_float_impl {
|
||||
($t:ty, $func:ident) => {
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl FromStr for $t {
|
||||
type Err = ParseFloatError;
|
||||
|
||||
/// Converts a string in base 10 to a float.
|
||||
/// Accepts an optional decimal exponent.
|
||||
///
|
||||
/// This function accepts strings such as
|
||||
///
|
||||
/// * '3.14'
|
||||
/// * '-3.14'
|
||||
/// * '2.5E10', or equivalently, '2.5e10'
|
||||
/// * '2.5E-10'
|
||||
/// * '.' (understood as 0)
|
||||
/// * '5.'
|
||||
/// * '.5', or, equivalently, '0.5'
|
||||
/// * 'inf', '-inf', 'NaN'
|
||||
///
|
||||
/// Leading and trailing whitespace represent an error.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * src - A string
|
||||
///
|
||||
/// # Return value
|
||||
///
|
||||
/// `Err(ParseFloatError)` if the string did not represent a valid
|
||||
/// number. Otherwise, `Ok(n)` where `n` is the floating-point
|
||||
/// number represented by `src`.
|
||||
#[inline]
|
||||
fn from_str(src: &str) -> Result<Self, ParseFloatError> {
|
||||
dec2flt(src)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
from_str_float_impl!(f32, to_f32);
|
||||
from_str_float_impl!(f64, to_f64);
|
||||
|
||||
/// An error which can be returned when parsing a float.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub struct ParseFloatError {
|
||||
kind: FloatErrorKind
|
||||
}
|
||||
|
||||
/// Entry point for decimal-to-f64 conversion.
|
||||
pub fn to_f64(s: &str) -> Result<f64, PFE> {
|
||||
dec2flt(s)
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
enum FloatErrorKind {
|
||||
Empty,
|
||||
Invalid,
|
||||
}
|
||||
|
||||
impl ParseFloatError {
|
||||
#[unstable(feature = "int_error_internals",
|
||||
reason = "available through Error trait and this method should \
|
||||
not be exposed publicly",
|
||||
issue = "0")]
|
||||
#[doc(hidden)]
|
||||
pub fn __description(&self) -> &str {
|
||||
match self.kind {
|
||||
FloatErrorKind::Empty => "cannot parse float from empty string",
|
||||
FloatErrorKind::Invalid => "invalid float literal",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl fmt::Display for ParseFloatError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.__description().fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pfe_empty() -> ParseFloatError {
|
||||
ParseFloatError { kind: FloatErrorKind::Empty }
|
||||
}
|
||||
|
||||
pub fn pfe_invalid() -> ParseFloatError {
|
||||
ParseFloatError { kind: FloatErrorKind::Invalid }
|
||||
}
|
||||
|
||||
/// Split decimal string into sign and the rest, without inspecting or validating the rest.
|
||||
|
@ -131,9 +205,9 @@ fn extract_sign(s: &str) -> (Sign, &str) {
|
|||
}
|
||||
|
||||
/// Convert a decimal string into a floating point number.
|
||||
fn dec2flt<T: RawFloat>(s: &str) -> Result<T, PFE> {
|
||||
fn dec2flt<T: RawFloat>(s: &str) -> Result<T, ParseFloatError> {
|
||||
if s.is_empty() {
|
||||
return Err(PFE { __kind: FloatErrorKind::Empty });
|
||||
return Err(pfe_empty())
|
||||
}
|
||||
let (sign, s) = extract_sign(s);
|
||||
let flt = match parse_decimal(s) {
|
||||
|
@ -143,7 +217,7 @@ fn dec2flt<T: RawFloat>(s: &str) -> Result<T, PFE> {
|
|||
ParseResult::Invalid => match s {
|
||||
"inf" => T::infinity(),
|
||||
"NaN" => T::nan(),
|
||||
_ => { return Err(PFE { __kind: FloatErrorKind::Invalid }); }
|
||||
_ => { return Err(pfe_invalid()); }
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -155,7 +229,7 @@ fn dec2flt<T: RawFloat>(s: &str) -> Result<T, PFE> {
|
|||
|
||||
/// The main workhorse for the decimal-to-float conversion: Orchestrate all the preprocessing
|
||||
/// and figure out which algorithm should do the actual conversion.
|
||||
fn convert<T: RawFloat>(mut decimal: Decimal) -> Result<T, PFE> {
|
||||
fn convert<T: RawFloat>(mut decimal: Decimal) -> Result<T, ParseFloatError> {
|
||||
simplify(&mut decimal);
|
||||
if let Some(x) = trivial_cases(&decimal) {
|
||||
return Ok(x);
|
||||
|
@ -172,7 +246,7 @@ fn convert<T: RawFloat>(mut decimal: Decimal) -> Result<T, PFE> {
|
|||
// If we exceed this, perhaps while calculating `f * 10^e` in Algorithm R or Algorithm M,
|
||||
// we'll crash. So we error out before getting too close, with a generous safety margin.
|
||||
if max_digits > 375 {
|
||||
return Err(PFE { __kind: FloatErrorKind::Invalid });
|
||||
return Err(pfe_invalid());
|
||||
}
|
||||
let f = digits_to_big(decimal.integral, decimal.fractional);
|
||||
|
||||
|
|
|
@ -23,8 +23,7 @@ macro_rules! from_str_radix_float_impl {
|
|||
($T:ty) => {
|
||||
fn from_str_radix(src: &str, radix: u32)
|
||||
-> Result<$T, ParseFloatError> {
|
||||
use num::FloatErrorKind::*;
|
||||
use num::ParseFloatError as PFE;
|
||||
use num::dec2flt::{pfe_empty, pfe_invalid};
|
||||
|
||||
// Special values
|
||||
match src {
|
||||
|
@ -35,8 +34,8 @@ macro_rules! from_str_radix_float_impl {
|
|||
}
|
||||
|
||||
let (is_positive, src) = match src.slice_shift_char() {
|
||||
None => return Err(PFE { __kind: Empty }),
|
||||
Some(('-', "")) => return Err(PFE { __kind: Empty }),
|
||||
None => return Err(pfe_empty()),
|
||||
Some(('-', "")) => return Err(pfe_empty()),
|
||||
Some(('-', src)) => (false, src),
|
||||
Some((_, _)) => (true, src),
|
||||
};
|
||||
|
@ -88,7 +87,7 @@ macro_rules! from_str_radix_float_impl {
|
|||
break; // start of fractional part
|
||||
},
|
||||
_ => {
|
||||
return Err(PFE { __kind: Invalid });
|
||||
return Err(pfe_invalid())
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -122,7 +121,7 @@ macro_rules! from_str_radix_float_impl {
|
|||
break; // start of exponent
|
||||
},
|
||||
_ => {
|
||||
return Err(PFE { __kind: Invalid });
|
||||
return Err(pfe_invalid())
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -135,7 +134,7 @@ macro_rules! from_str_radix_float_impl {
|
|||
let base = match c {
|
||||
'E' | 'e' if radix == 10 => 10.0,
|
||||
'P' | 'p' if radix == 16 => 2.0,
|
||||
_ => return Err(PFE { __kind: Invalid }),
|
||||
_ => return Err(pfe_invalid()),
|
||||
};
|
||||
|
||||
// Parse the exponent as decimal integer
|
||||
|
@ -144,13 +143,13 @@ macro_rules! from_str_radix_float_impl {
|
|||
Some(('-', src)) => (false, src.parse::<usize>()),
|
||||
Some(('+', src)) => (true, src.parse::<usize>()),
|
||||
Some((_, _)) => (true, src.parse::<usize>()),
|
||||
None => return Err(PFE { __kind: Invalid }),
|
||||
None => return Err(pfe_invalid()),
|
||||
};
|
||||
|
||||
match (is_positive, exp) {
|
||||
(true, Ok(exp)) => base.powi(exp as i32),
|
||||
(false, Ok(exp)) => 1.0 / base.powi(exp as i32),
|
||||
(_, Err(_)) => return Err(PFE { __kind: Invalid }),
|
||||
(_, Err(_)) => return Err(pfe_invalid()),
|
||||
}
|
||||
},
|
||||
None => 1.0, // no exponent
|
||||
|
|
|
@ -1365,47 +1365,6 @@ pub trait Float: Sized {
|
|||
fn to_radians(self) -> Self;
|
||||
}
|
||||
|
||||
macro_rules! from_str_float_impl {
|
||||
($t:ty, $func:ident) => {
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl FromStr for $t {
|
||||
type Err = ParseFloatError;
|
||||
|
||||
/// Converts a string in base 10 to a float.
|
||||
/// Accepts an optional decimal exponent.
|
||||
///
|
||||
/// This function accepts strings such as
|
||||
///
|
||||
/// * '3.14'
|
||||
/// * '-3.14'
|
||||
/// * '2.5E10', or equivalently, '2.5e10'
|
||||
/// * '2.5E-10'
|
||||
/// * '.' (understood as 0)
|
||||
/// * '5.'
|
||||
/// * '.5', or, equivalently, '0.5'
|
||||
/// * 'inf', '-inf', 'NaN'
|
||||
///
|
||||
/// Leading and trailing whitespace represent an error.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * src - A string
|
||||
///
|
||||
/// # Return value
|
||||
///
|
||||
/// `Err(ParseFloatError)` if the string did not represent a valid
|
||||
/// number. Otherwise, `Ok(n)` where `n` is the floating-point
|
||||
/// number represented by `src`.
|
||||
#[inline]
|
||||
fn from_str(src: &str) -> Result<Self, ParseFloatError> {
|
||||
dec2flt::$func(src)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
from_str_float_impl!(f32, to_f32);
|
||||
from_str_float_impl!(f64, to_f64);
|
||||
|
||||
macro_rules! from_str_radix_int_impl {
|
||||
($($t:ty)*) => {$(
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
|
@ -1548,40 +1507,4 @@ impl fmt::Display for ParseIntError {
|
|||
}
|
||||
}
|
||||
|
||||
/// An error which can be returned when parsing a float.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub struct ParseFloatError {
|
||||
#[doc(hidden)]
|
||||
#[unstable(feature = "float_error_internals",
|
||||
reason = "should not be exposed publicly",
|
||||
issue = "0")]
|
||||
pub __kind: FloatErrorKind
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[unstable(feature = "float_error_internals",
|
||||
reason = "should not be exposed publicly",
|
||||
issue = "0")]
|
||||
#[doc(hidden)]
|
||||
pub enum FloatErrorKind {
|
||||
Empty,
|
||||
Invalid,
|
||||
}
|
||||
|
||||
impl ParseFloatError {
|
||||
#[doc(hidden)]
|
||||
pub fn __description(&self) -> &str {
|
||||
match self.__kind {
|
||||
FloatErrorKind::Empty => "cannot parse float from empty string",
|
||||
FloatErrorKind::Invalid => "invalid float literal",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl fmt::Display for ParseFloatError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.__description().fmt(f)
|
||||
}
|
||||
}
|
||||
pub use num::dec2flt::ParseFloatError;
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use core::atomic::*;
|
||||
use core::atomic::Ordering::SeqCst;
|
||||
use core::sync::atomic::*;
|
||||
use core::sync::atomic::Ordering::SeqCst;
|
||||
|
||||
#[test]
|
||||
fn bool_() {
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
|
||||
use std::{i64, f32, f64};
|
||||
use test;
|
||||
use core::num::dec2flt::{to_f32, to_f64};
|
||||
|
||||
mod parse;
|
||||
mod rawfp;
|
||||
|
@ -27,11 +26,11 @@ macro_rules! test_literal {
|
|||
let inputs = &[stringify!($x).into(), format!("{:?}", x64), format!("{:e}", x64)];
|
||||
for input in inputs {
|
||||
if input != "inf" {
|
||||
assert_eq!(to_f64(input), Ok(x64));
|
||||
assert_eq!(to_f32(input), Ok(x32));
|
||||
assert_eq!(input.parse(), Ok(x64));
|
||||
assert_eq!(input.parse(), Ok(x32));
|
||||
let neg_input = &format!("-{}", input);
|
||||
assert_eq!(to_f64(neg_input), Ok(-x64));
|
||||
assert_eq!(to_f32(neg_input), Ok(-x32));
|
||||
assert_eq!(neg_input.parse(), Ok(-x64));
|
||||
assert_eq!(neg_input.parse(), Ok(-x32));
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -99,83 +98,83 @@ fn fast_path_correct() {
|
|||
|
||||
#[test]
|
||||
fn lonely_dot() {
|
||||
assert_eq!(to_f64("."), Ok(0.0));
|
||||
assert_eq!(".".parse(), Ok(0.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nan() {
|
||||
assert!(to_f64("NaN").unwrap().is_nan());
|
||||
assert!(to_f32("NaN").unwrap().is_nan());
|
||||
assert!("NaN".parse::<f32>().unwrap().is_nan());
|
||||
assert!("NaN".parse::<f64>().unwrap().is_nan());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inf() {
|
||||
assert_eq!(to_f64("inf"), Ok(f64::INFINITY));
|
||||
assert_eq!(to_f64("-inf"), Ok(f64::NEG_INFINITY));
|
||||
assert_eq!(to_f32("inf"), Ok(f32::INFINITY));
|
||||
assert_eq!(to_f32("-inf"), Ok(f32::NEG_INFINITY));
|
||||
assert_eq!("inf".parse(), Ok(f64::INFINITY));
|
||||
assert_eq!("-inf".parse(), Ok(f64::NEG_INFINITY));
|
||||
assert_eq!("inf".parse(), Ok(f32::INFINITY));
|
||||
assert_eq!("-inf".parse(), Ok(f32::NEG_INFINITY));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn massive_exponent() {
|
||||
let max = i64::MAX;
|
||||
assert_eq!(to_f64(&format!("1e{}000", max)), Ok(f64::INFINITY));
|
||||
assert_eq!(to_f64(&format!("1e-{}000", max)), Ok(0.0));
|
||||
assert_eq!(to_f64(&format!("1e{}000", max)), Ok(f64::INFINITY));
|
||||
assert_eq!(format!("1e{}000", max).parse(), Ok(f64::INFINITY));
|
||||
assert_eq!(format!("1e-{}000", max).parse(), Ok(0.0));
|
||||
assert_eq!(format!("1e{}000", max).parse(), Ok(f64::INFINITY));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_0(b: &mut test::Bencher) {
|
||||
b.iter(|| to_f64("0.0"));
|
||||
b.iter(|| "0.0".parse::<f64>());
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_42(b: &mut test::Bencher) {
|
||||
b.iter(|| to_f64("42"));
|
||||
b.iter(|| "42".parse::<f64>());
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_huge_int(b: &mut test::Bencher) {
|
||||
// 2^128 - 1
|
||||
b.iter(|| to_f64("170141183460469231731687303715884105727"));
|
||||
b.iter(|| "170141183460469231731687303715884105727".parse::<f64>());
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_short_decimal(b: &mut test::Bencher) {
|
||||
b.iter(|| to_f64("1234.5678"));
|
||||
b.iter(|| "1234.5678".parse::<f64>());
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_pi_long(b: &mut test::Bencher) {
|
||||
b.iter(|| to_f64("3.14159265358979323846264338327950288"));
|
||||
b.iter(|| "3.14159265358979323846264338327950288".parse::<f64>());
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_pi_short(b: &mut test::Bencher) {
|
||||
b.iter(|| to_f64("3.141592653589793"))
|
||||
b.iter(|| "3.141592653589793".parse::<f64>())
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_1e150(b: &mut test::Bencher) {
|
||||
b.iter(|| to_f64("1e150"));
|
||||
b.iter(|| "1e150".parse::<f64>());
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_long_decimal_and_exp(b: &mut test::Bencher) {
|
||||
b.iter(|| to_f64("727501488517303786137132964064381141071e-123"));
|
||||
b.iter(|| "727501488517303786137132964064381141071e-123".parse::<f64>());
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_min_subnormal(b: &mut test::Bencher) {
|
||||
b.iter(|| to_f64("5e-324"));
|
||||
b.iter(|| "5e-324".parse::<f64>());
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_min_normal(b: &mut test::Bencher) {
|
||||
b.iter(|| to_f64("2.2250738585072014e-308"));
|
||||
b.iter(|| "2.2250738585072014e-308".parse::<f64>());
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_max(b: &mut test::Bencher) {
|
||||
b.iter(|| to_f64("1.7976931348623157e308"));
|
||||
b.iter(|| "1.7976931348623157e308".parse::<f64>());
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ mod tests {
|
|||
use core::$T_i::*;
|
||||
use num;
|
||||
use core::ops::{BitOr, BitAnd, BitXor, Shl, Shr, Not};
|
||||
use std::str;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[test]
|
||||
fn test_overflows() {
|
||||
|
@ -152,5 +152,5 @@ mod tests {
|
|||
assert_eq!($T::from_str_radix("Z", 10).ok(), None::<$T>);
|
||||
assert_eq!($T::from_str_radix("_", 2).ok(), None::<$T>);
|
||||
}
|
||||
|
||||
}
|
||||
)}
|
||||
|
|
|
@ -17,6 +17,6 @@ mod foo {
|
|||
fn assert_clone<T>() where T : Clone { }
|
||||
|
||||
fn main() {
|
||||
assert_clone::<foo::core::atomic::AtomicBool>();
|
||||
assert_clone::<foo::core::sync::atomic::AtomicBool>();
|
||||
//~^ ERROR the trait `foo::core::clone::Clone` is not implemented for the type `foo::core::
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,6 @@ extern crate core as bar;
|
|||
fn assert_clone<T>() where T : Clone { }
|
||||
|
||||
fn main() {
|
||||
assert_clone::<bar::atomic::AtomicBool>();
|
||||
//~^ ERROR the trait `bar::clone::Clone` is not implemented for the type `bar::atomic::
|
||||
}
|
||||
assert_clone::<bar::sync::atomic::AtomicBool>();
|
||||
//~^ ERROR the trait `bar::clone::Clone` is not implemented for the type `bar::sync::atomic::
|
||||
}
|
||||
|
|
|
@ -19,6 +19,6 @@ extern crate core;
|
|||
fn assert_clone<T>() where T : Clone { }
|
||||
|
||||
fn main() {
|
||||
assert_clone::<foo::core::atomic::AtomicBool>();
|
||||
//~^ ERROR the trait `core::clone::Clone` is not implemented for the type `core::atomic::
|
||||
}
|
||||
assert_clone::<foo::core::sync::atomic::AtomicBool>();
|
||||
//~^ ERROR the trait `core::clone::Clone` is not implemented for the type `core::sync::atomic::
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue