#![doc(hidden)]
#![unstable(feature = "dec2flt",
reason = "internal routines only exposed for testing",
issue = "0")]
use fmt;
use str::FromStr;
use self::parse::{parse_decimal, Decimal, Sign, ParseResult};
use self::num::digits_to_big;
use self::rawfp::RawFloat;
mod algorithm;
mod table;
mod num;
pub mod rawfp;
pub mod parse;
macro_rules! from_str_float_impl {
($t:ty) => {
#[stable(feature = "rust1", since = "1.0.0")]
impl FromStr for $t {
type Err = ParseFloatError;
#[inline]
fn from_str(src: &str) -> Result<Self, ParseFloatError> {
dec2flt(src)
}
}
}
}
from_str_float_impl!(f32);
from_str_float_impl!(f64);
#[derive(Debug, Clone, PartialEq, Eq)]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct ParseFloatError {
kind: FloatErrorKind
}
#[derive(Debug, Clone, PartialEq, Eq)]
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)
}
}
fn pfe_empty() -> ParseFloatError {
ParseFloatError { kind: FloatErrorKind::Empty }
}
fn pfe_invalid() -> ParseFloatError {
ParseFloatError { kind: FloatErrorKind::Invalid }
}
fn extract_sign(s: &str) -> (Sign, &str) {
match s.as_bytes()[0] {
b'+' => (Sign::Positive, &s[1..]),
b'-' => (Sign::Negative, &s[1..]),
_ => (Sign::Positive, s),
}
}
fn dec2flt<T: RawFloat>(s: &str) -> Result<T, ParseFloatError> {
if s.is_empty() {
return Err(pfe_empty())
}
let (sign, s) = extract_sign(s);
let flt = match parse_decimal(s) {
ParseResult::Valid(decimal) => convert(decimal)?,
ParseResult::ShortcutToInf => T::INFINITY,
ParseResult::ShortcutToZero => T::ZERO,
ParseResult::Invalid => match s {
"inf" => T::INFINITY,
"NaN" => T::NAN,
_ => { return Err(pfe_invalid()); }
}
};
match sign {
Sign::Positive => Ok(flt),
Sign::Negative => Ok(-flt),
}
}
fn convert<T: RawFloat>(mut decimal: Decimal) -> Result<T, ParseFloatError> {
simplify(&mut decimal);
if let Some(x) = trivial_cases(&decimal) {
return Ok(x);
}
let e = decimal.exp - decimal.fractional.len() as i64;
if let Some(x) = algorithm::fast_path(decimal.integral, decimal.fractional, e) {
return Ok(x);
}
let upper_bound = bound_intermediate_digits(&decimal, e);
if upper_bound > 375 {
return Err(pfe_invalid());
}
let f = digits_to_big(decimal.integral, decimal.fractional);
let e = e as i16;
let exponent_in_range = table::MIN_E <= e && e <= table::MAX_E;
let value_in_range = upper_bound <= T::MAX_NORMAL_DIGITS as u64;
if exponent_in_range && value_in_range {
Ok(algorithm::bellerophon(&f, e))
} else {
Ok(algorithm::algorithm_m(&f, e))
}
}
#[inline(always)]
fn simplify(decimal: &mut Decimal) {
let is_zero = &|&&d: &&u8| -> bool { d == b'0' };
let leading_zeros = decimal.integral.iter().take_while(is_zero).count();
decimal.integral = &decimal.integral[leading_zeros..];
let trailing_zeros = decimal.fractional.iter().rev().take_while(is_zero).count();
let end = decimal.fractional.len() - trailing_zeros;
decimal.fractional = &decimal.fractional[..end];
if decimal.integral.is_empty() {
let leading_zeros = decimal.fractional.iter().take_while(is_zero).count();
decimal.fractional = &decimal.fractional[leading_zeros..];
decimal.exp -= leading_zeros as i64;
} else if decimal.fractional.is_empty() {
let trailing_zeros = decimal.integral.iter().rev().take_while(is_zero).count();
let end = decimal.integral.len() - trailing_zeros;
decimal.integral = &decimal.integral[..end];
decimal.exp += trailing_zeros as i64;
}
}
fn bound_intermediate_digits(decimal: &Decimal, e: i64) -> u64 {
let f_len: u64 = decimal.integral.len() as u64 + decimal.fractional.len() as u64;
if e >= 0 {
f_len + (e as u64)
} else {
f_len + (e.abs() as u64) + 17
}
}
fn trivial_cases<T: RawFloat>(decimal: &Decimal) -> Option<T> {
if decimal.integral.is_empty() && decimal.fractional.is_empty() {
return Some(T::ZERO);
}
let max_place = decimal.exp + decimal.integral.len() as i64;
if max_place > T::INF_CUTOFF {
return Some(T::INFINITY);
} else if max_place < T::ZERO_CUTOFF {
return Some(T::ZERO);
}
None
}