Moved numeric string conversion functions into own module
This commit is contained in:
parent
bf27352953
commit
adac6cb5c6
7 changed files with 642 additions and 604 deletions
|
@ -14,6 +14,7 @@ use cmath;
|
|||
use cmp;
|
||||
use libc::{c_float, c_int};
|
||||
use num::NumCast;
|
||||
use num::strconv;
|
||||
use num;
|
||||
use ops;
|
||||
use option::Option;
|
||||
|
@ -376,8 +377,8 @@ impl num::Round for f32 {
|
|||
*/
|
||||
#[inline(always)]
|
||||
pub pure fn to_str(num: f32) -> ~str {
|
||||
let (r, _) = num::to_str_common(
|
||||
&num, 10u, true, true, num::SignNeg, num::DigAll);
|
||||
let (r, _) = strconv::to_str_common(
|
||||
&num, 10u, true, true, strconv::SignNeg, strconv::DigAll);
|
||||
r
|
||||
}
|
||||
|
||||
|
@ -390,8 +391,8 @@ pub pure fn to_str(num: f32) -> ~str {
|
|||
*/
|
||||
#[inline(always)]
|
||||
pub pure fn to_str_hex(num: f32) -> ~str {
|
||||
let (r, _) = num::to_str_common(
|
||||
&num, 16u, true, true, num::SignNeg, num::DigAll);
|
||||
let (r, _) = strconv::to_str_common(
|
||||
&num, 16u, true, true, strconv::SignNeg, strconv::DigAll);
|
||||
r
|
||||
}
|
||||
|
||||
|
@ -411,8 +412,8 @@ pub pure fn to_str_hex(num: f32) -> ~str {
|
|||
*/
|
||||
#[inline(always)]
|
||||
pub pure fn to_str_radix(num: f32, rdx: uint) -> ~str {
|
||||
let (r, special) = num::to_str_common(
|
||||
&num, rdx, true, true, num::SignNeg, num::DigAll);
|
||||
let (r, special) = strconv::to_str_common(
|
||||
&num, rdx, true, true, strconv::SignNeg, strconv::DigAll);
|
||||
if special { fail!(~"number has a special value, \
|
||||
try to_str_radix_special() if those are expected") }
|
||||
r
|
||||
|
@ -429,7 +430,8 @@ pub pure fn to_str_radix(num: f32, rdx: uint) -> ~str {
|
|||
*/
|
||||
#[inline(always)]
|
||||
pub pure fn to_str_radix_special(num: f32, rdx: uint) -> (~str, bool) {
|
||||
num::to_str_common(&num, rdx, true, true, num::SignNeg, num::DigAll)
|
||||
strconv::to_str_common(&num, rdx, true, true,
|
||||
strconv::SignNeg, strconv::DigAll)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -443,8 +445,8 @@ pub pure fn to_str_radix_special(num: f32, rdx: uint) -> (~str, bool) {
|
|||
*/
|
||||
#[inline(always)]
|
||||
pub pure fn to_str_exact(num: f32, dig: uint) -> ~str {
|
||||
let (r, _) = num::to_str_common(
|
||||
&num, 10u, true, true, num::SignNeg, num::DigExact(dig));
|
||||
let (r, _) = strconv::to_str_common(
|
||||
&num, 10u, true, true, strconv::SignNeg, strconv::DigExact(dig));
|
||||
r
|
||||
}
|
||||
|
||||
|
@ -459,8 +461,8 @@ pub pure fn to_str_exact(num: f32, dig: uint) -> ~str {
|
|||
*/
|
||||
#[inline(always)]
|
||||
pub pure fn to_str_digits(num: f32, dig: uint) -> ~str {
|
||||
let (r, _) = num::to_str_common(
|
||||
&num, 10u, true, true, num::SignNeg, num::DigMax(dig));
|
||||
let (r, _) = strconv::to_str_common(
|
||||
&num, 10u, true, true, strconv::SignNeg, strconv::DigMax(dig));
|
||||
r
|
||||
}
|
||||
|
||||
|
@ -505,7 +507,8 @@ impl num::ToStrRadix for f32 {
|
|||
*/
|
||||
#[inline(always)]
|
||||
pub pure fn from_str(num: &str) -> Option<f32> {
|
||||
num::from_str_common(num, 10u, true, true, true, num::ExpDec, false)
|
||||
strconv::from_str_common(num, 10u, true, true, true,
|
||||
strconv::ExpDec, false)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -537,7 +540,8 @@ pub pure fn from_str(num: &str) -> Option<f32> {
|
|||
*/
|
||||
#[inline(always)]
|
||||
pub pure fn from_str_hex(num: &str) -> Option<f32> {
|
||||
num::from_str_common(num, 16u, true, true, true, num::ExpBin, false)
|
||||
strconv::from_str_common(num, 16u, true, true, true,
|
||||
strconv::ExpBin, false)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -561,7 +565,8 @@ pub pure fn from_str_hex(num: &str) -> Option<f32> {
|
|||
*/
|
||||
#[inline(always)]
|
||||
pub pure fn from_str_radix(num: &str, rdx: uint) -> Option<f32> {
|
||||
num::from_str_common(num, rdx, true, true, false, num::ExpNone, false)
|
||||
strconv::from_str_common(num, rdx, true, true, false,
|
||||
strconv::ExpNone, false)
|
||||
}
|
||||
|
||||
impl from_str::FromStr for f32 {
|
||||
|
|
|
@ -15,6 +15,7 @@ use cmp;
|
|||
use libc::{c_double, c_int};
|
||||
use libc;
|
||||
use num::NumCast;
|
||||
use num::strconv;
|
||||
use num;
|
||||
use ops;
|
||||
use option::Option;
|
||||
|
@ -401,8 +402,8 @@ impl num::Round for f64 {
|
|||
*/
|
||||
#[inline(always)]
|
||||
pub pure fn to_str(num: f64) -> ~str {
|
||||
let (r, _) = num::to_str_common(
|
||||
&num, 10u, true, true, num::SignNeg, num::DigAll);
|
||||
let (r, _) = strconv::to_str_common(
|
||||
&num, 10u, true, true, strconv::SignNeg, strconv::DigAll);
|
||||
r
|
||||
}
|
||||
|
||||
|
@ -415,8 +416,8 @@ pub pure fn to_str(num: f64) -> ~str {
|
|||
*/
|
||||
#[inline(always)]
|
||||
pub pure fn to_str_hex(num: f64) -> ~str {
|
||||
let (r, _) = num::to_str_common(
|
||||
&num, 16u, true, true, num::SignNeg, num::DigAll);
|
||||
let (r, _) = strconv::to_str_common(
|
||||
&num, 16u, true, true, strconv::SignNeg, strconv::DigAll);
|
||||
r
|
||||
}
|
||||
|
||||
|
@ -436,8 +437,8 @@ pub pure fn to_str_hex(num: f64) -> ~str {
|
|||
*/
|
||||
#[inline(always)]
|
||||
pub pure fn to_str_radix(num: f64, rdx: uint) -> ~str {
|
||||
let (r, special) = num::to_str_common(
|
||||
&num, rdx, true, true, num::SignNeg, num::DigAll);
|
||||
let (r, special) = strconv::to_str_common(
|
||||
&num, rdx, true, true, strconv::SignNeg, strconv::DigAll);
|
||||
if special { fail!(~"number has a special value, \
|
||||
try to_str_radix_special() if those are expected") }
|
||||
r
|
||||
|
@ -454,7 +455,8 @@ pub pure fn to_str_radix(num: f64, rdx: uint) -> ~str {
|
|||
*/
|
||||
#[inline(always)]
|
||||
pub pure fn to_str_radix_special(num: f64, rdx: uint) -> (~str, bool) {
|
||||
num::to_str_common(&num, rdx, true, true, num::SignNeg, num::DigAll)
|
||||
strconv::to_str_common(&num, rdx, true, true,
|
||||
strconv::SignNeg, strconv::DigAll)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -468,8 +470,8 @@ pub pure fn to_str_radix_special(num: f64, rdx: uint) -> (~str, bool) {
|
|||
*/
|
||||
#[inline(always)]
|
||||
pub pure fn to_str_exact(num: f64, dig: uint) -> ~str {
|
||||
let (r, _) = num::to_str_common(
|
||||
&num, 10u, true, true, num::SignNeg, num::DigExact(dig));
|
||||
let (r, _) = strconv::to_str_common(
|
||||
&num, 10u, true, true, strconv::SignNeg, strconv::DigExact(dig));
|
||||
r
|
||||
}
|
||||
|
||||
|
@ -484,8 +486,8 @@ pub pure fn to_str_exact(num: f64, dig: uint) -> ~str {
|
|||
*/
|
||||
#[inline(always)]
|
||||
pub pure fn to_str_digits(num: f64, dig: uint) -> ~str {
|
||||
let (r, _) = num::to_str_common(
|
||||
&num, 10u, true, true, num::SignNeg, num::DigMax(dig));
|
||||
let (r, _) = strconv::to_str_common(
|
||||
&num, 10u, true, true, strconv::SignNeg, strconv::DigMax(dig));
|
||||
r
|
||||
}
|
||||
|
||||
|
@ -530,7 +532,8 @@ impl num::ToStrRadix for f64 {
|
|||
*/
|
||||
#[inline(always)]
|
||||
pub pure fn from_str(num: &str) -> Option<f64> {
|
||||
num::from_str_common(num, 10u, true, true, true, num::ExpDec, false)
|
||||
strconv::from_str_common(num, 10u, true, true, true,
|
||||
strconv::ExpDec, false)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -562,7 +565,8 @@ pub pure fn from_str(num: &str) -> Option<f64> {
|
|||
*/
|
||||
#[inline(always)]
|
||||
pub pure fn from_str_hex(num: &str) -> Option<f64> {
|
||||
num::from_str_common(num, 16u, true, true, true, num::ExpBin, false)
|
||||
strconv::from_str_common(num, 16u, true, true, true,
|
||||
strconv::ExpBin, false)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -586,7 +590,8 @@ pub pure fn from_str_hex(num: &str) -> Option<f64> {
|
|||
*/
|
||||
#[inline(always)]
|
||||
pub pure fn from_str_radix(num: &str, rdx: uint) -> Option<f64> {
|
||||
num::from_str_common(num, rdx, true, true, false, num::ExpNone, false)
|
||||
strconv::from_str_common(num, rdx, true, true, false,
|
||||
strconv::ExpNone, false)
|
||||
}
|
||||
|
||||
impl from_str::FromStr for f64 {
|
||||
|
|
|
@ -26,6 +26,7 @@ use cmp::{Eq, Ord};
|
|||
use cmp;
|
||||
use f64;
|
||||
use num::NumCast;
|
||||
use num::strconv;
|
||||
use num;
|
||||
use ops;
|
||||
use option::{None, Option, Some};
|
||||
|
@ -107,8 +108,8 @@ pub mod consts {
|
|||
*/
|
||||
#[inline(always)]
|
||||
pub pure fn to_str(num: float) -> ~str {
|
||||
let (r, _) = num::to_str_common(
|
||||
&num, 10u, true, true, num::SignNeg, num::DigAll);
|
||||
let (r, _) = strconv::to_str_common(
|
||||
&num, 10u, true, true, strconv::SignNeg, strconv::DigAll);
|
||||
r
|
||||
}
|
||||
|
||||
|
@ -121,8 +122,8 @@ pub pure fn to_str(num: float) -> ~str {
|
|||
*/
|
||||
#[inline(always)]
|
||||
pub pure fn to_str_hex(num: float) -> ~str {
|
||||
let (r, _) = num::to_str_common(
|
||||
&num, 16u, true, true, num::SignNeg, num::DigAll);
|
||||
let (r, _) = strconv::to_str_common(
|
||||
&num, 16u, true, true, strconv::SignNeg, strconv::DigAll);
|
||||
r
|
||||
}
|
||||
|
||||
|
@ -142,8 +143,8 @@ pub pure fn to_str_hex(num: float) -> ~str {
|
|||
*/
|
||||
#[inline(always)]
|
||||
pub pure fn to_str_radix(num: float, radix: uint) -> ~str {
|
||||
let (r, special) = num::to_str_common(
|
||||
&num, radix, true, true, num::SignNeg, num::DigAll);
|
||||
let (r, special) = strconv::to_str_common(
|
||||
&num, radix, true, true, strconv::SignNeg, strconv::DigAll);
|
||||
if special { fail!(~"number has a special value, \
|
||||
try to_str_radix_special() if those are expected") }
|
||||
r
|
||||
|
@ -160,7 +161,8 @@ pub pure fn to_str_radix(num: float, radix: uint) -> ~str {
|
|||
*/
|
||||
#[inline(always)]
|
||||
pub pure fn to_str_radix_special(num: float, radix: uint) -> (~str, bool) {
|
||||
num::to_str_common(&num, radix, true, true, num::SignNeg, num::DigAll)
|
||||
strconv::to_str_common(&num, radix, true, true,
|
||||
strconv::SignNeg, strconv::DigAll)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -174,8 +176,8 @@ pub pure fn to_str_radix_special(num: float, radix: uint) -> (~str, bool) {
|
|||
*/
|
||||
#[inline(always)]
|
||||
pub pure fn to_str_exact(num: float, digits: uint) -> ~str {
|
||||
let (r, _) = num::to_str_common(
|
||||
&num, 10u, true, true, num::SignNeg, num::DigExact(digits));
|
||||
let (r, _) = strconv::to_str_common(
|
||||
&num, 10u, true, true, strconv::SignNeg, strconv::DigExact(digits));
|
||||
r
|
||||
}
|
||||
|
||||
|
@ -196,8 +198,8 @@ pub fn test_to_str_exact_do_decimal() {
|
|||
*/
|
||||
#[inline(always)]
|
||||
pub pure fn to_str_digits(num: float, digits: uint) -> ~str {
|
||||
let (r, _) = num::to_str_common(
|
||||
&num, 10u, true, true, num::SignNeg, num::DigMax(digits));
|
||||
let (r, _) = strconv::to_str_common(
|
||||
&num, 10u, true, true, strconv::SignNeg, strconv::DigMax(digits));
|
||||
r
|
||||
}
|
||||
|
||||
|
@ -242,7 +244,8 @@ impl num::ToStrRadix for float {
|
|||
*/
|
||||
#[inline(always)]
|
||||
pub pure fn from_str(num: &str) -> Option<float> {
|
||||
num::from_str_common(num, 10u, true, true, true, num::ExpDec, false)
|
||||
strconv::from_str_common(num, 10u, true, true, true,
|
||||
strconv::ExpDec, false)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -274,7 +277,8 @@ pub pure fn from_str(num: &str) -> Option<float> {
|
|||
*/
|
||||
#[inline(always)]
|
||||
pub pure fn from_str_hex(num: &str) -> Option<float> {
|
||||
num::from_str_common(num, 16u, true, true, true, num::ExpBin, false)
|
||||
strconv::from_str_common(num, 16u, true, true, true,
|
||||
strconv::ExpBin, false)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -298,7 +302,8 @@ pub pure fn from_str_hex(num: &str) -> Option<float> {
|
|||
*/
|
||||
#[inline(always)]
|
||||
pub pure fn from_str_radix(num: &str, radix: uint) -> Option<float> {
|
||||
num::from_str_common(num, radix, true, true, false, num::ExpNone, false)
|
||||
strconv::from_str_common(num, radix, true, true, false,
|
||||
strconv::ExpNone, false)
|
||||
}
|
||||
|
||||
impl from_str::FromStr for float {
|
||||
|
|
|
@ -16,6 +16,7 @@ use cmp;
|
|||
use to_str::ToStr;
|
||||
use from_str::FromStr;
|
||||
use num::{ToStrRadix, FromStrRadix};
|
||||
use num::strconv;
|
||||
use num;
|
||||
use prelude::*;
|
||||
use str;
|
||||
|
@ -218,22 +219,22 @@ impl ops::Neg<T> for T {
|
|||
/// Parse a string as a number in base 10.
|
||||
#[inline(always)]
|
||||
pub pure fn from_str(s: &str) -> Option<T> {
|
||||
num::from_str_common(s, 10u, true, false, false,
|
||||
num::ExpNone, false)
|
||||
strconv::from_str_common(s, 10u, true, false, false,
|
||||
strconv::ExpNone, false)
|
||||
}
|
||||
|
||||
/// Parse a string as a number in the given base.
|
||||
#[inline(always)]
|
||||
pub pure fn from_str_radix(s: &str, radix: uint) -> Option<T> {
|
||||
num::from_str_common(s, radix, true, false, false,
|
||||
num::ExpNone, false)
|
||||
strconv::from_str_common(s, radix, true, false, false,
|
||||
strconv::ExpNone, false)
|
||||
}
|
||||
|
||||
/// Parse a byte slice as a number in the given base.
|
||||
#[inline(always)]
|
||||
pub pure fn parse_bytes(buf: &[u8], radix: uint) -> Option<T> {
|
||||
num::from_str_bytes_common(buf, radix, true, false, false,
|
||||
num::ExpNone, false)
|
||||
strconv::from_str_bytes_common(buf, radix, true, false, false,
|
||||
strconv::ExpNone, false)
|
||||
}
|
||||
|
||||
impl FromStr for T {
|
||||
|
@ -255,24 +256,24 @@ impl FromStrRadix for T {
|
|||
/// Convert to a string as a byte slice in a given base.
|
||||
#[inline(always)]
|
||||
pub pure fn to_str_bytes<U>(n: T, radix: uint, f: fn(v: &[u8]) -> U) -> U {
|
||||
let (buf, _) = num::to_str_bytes_common(&n, radix, false, false,
|
||||
num::SignNeg, num::DigAll);
|
||||
let (buf, _) = strconv::to_str_bytes_common(&n, radix, false, false,
|
||||
strconv::SignNeg, strconv::DigAll);
|
||||
f(buf)
|
||||
}
|
||||
|
||||
/// Convert to a string in base 10.
|
||||
#[inline(always)]
|
||||
pub pure fn to_str(num: T) -> ~str {
|
||||
let (buf, _) = num::to_str_common(&num, 10u, false, false,
|
||||
num::SignNeg, num::DigAll);
|
||||
let (buf, _) = strconv::to_str_common(&num, 10u, false, false,
|
||||
strconv::SignNeg, strconv::DigAll);
|
||||
buf
|
||||
}
|
||||
|
||||
/// Convert to a string in a given base.
|
||||
#[inline(always)]
|
||||
pub pure fn to_str_radix(num: T, radix: uint) -> ~str {
|
||||
let (buf, _) = num::to_str_common(&num, radix, false, false,
|
||||
num::SignNeg, num::DigAll);
|
||||
let (buf, _) = strconv::to_str_common(&num, radix, false, false,
|
||||
strconv::SignNeg, strconv::DigAll);
|
||||
buf
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
|
||||
// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
|
@ -17,6 +17,8 @@ use str;
|
|||
use kinds::Copy;
|
||||
use vec;
|
||||
|
||||
pub mod strconv;
|
||||
|
||||
pub trait IntConvertible {
|
||||
pure fn to_int(&self) -> int;
|
||||
static pure fn from_int(n: int) -> Self;
|
||||
|
@ -186,540 +188,3 @@ pub pure fn pow_with_uint<T:NumCast+One+Zero+Copy+Div<T,T>+Mul<T,T>>(
|
|||
total
|
||||
}
|
||||
|
||||
pub enum ExponentFormat {
|
||||
ExpNone,
|
||||
ExpDec,
|
||||
ExpBin
|
||||
}
|
||||
|
||||
pub enum SignificantDigits {
|
||||
DigAll,
|
||||
DigMax(uint),
|
||||
DigExact(uint)
|
||||
}
|
||||
|
||||
pub enum SignFormat {
|
||||
SignNone,
|
||||
SignNeg,
|
||||
SignAll
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a number to its string representation as a byte vector.
|
||||
* This is meant to be a common base implementation for all numeric string
|
||||
* conversion functions like `to_str()` or `to_str_radix()`.
|
||||
*
|
||||
* # Arguments
|
||||
* - `num` - The number to convert. Accepts any number that
|
||||
* implements the numeric traits.
|
||||
* - `radix` - Base to use. Accepts only the values 2-36.
|
||||
* - `special` - Whether to attempt to compare to special values like
|
||||
* `inf` or `NaN`. Also needed to detect negative 0.
|
||||
* Can fail if it doesn't match `num`s type
|
||||
* (see safety note).
|
||||
* - `negative_zero` - Whether to treat the special value `-0` as
|
||||
* `-0` or as `+0`.
|
||||
* - `sign` - How to emit the sign. Options are:
|
||||
* - `SignNone`: No sign at all. Basically emits `abs(num)`.
|
||||
* - `SignNeg`: Only `-` on negative values.
|
||||
* - `SignAll`: Both `+` on positive, and `-` on negative numbers.
|
||||
* - `digits` - The amount of digits to use for emitting the
|
||||
* fractional part, if any. Options are:
|
||||
* - `DigAll`: All calculatable digits. Beware of bignums or
|
||||
* fractions!
|
||||
* - `DigMax(uint)`: Maximum N digits, truncating any trailing zeros.
|
||||
* - `DigExact(uint)`: Exactly N digits.
|
||||
*
|
||||
* # Return value
|
||||
* A tuple containing the byte vector, and a boolean flag indicating
|
||||
* whether it represents a special value like `inf`, `-inf`, `NaN` or not.
|
||||
* It returns a tuple because there can be ambiguity between a special value
|
||||
* and a number representation at higher bases.
|
||||
*
|
||||
* # Failure
|
||||
* - Fails if `radix` < 2 or `radix` > 36.
|
||||
* - Fails on wrong value for `special` (see safety note).
|
||||
*
|
||||
* # Safety note
|
||||
* The function detects the special values `inf`, `-inf` and `NaN` by
|
||||
* dynamically comparing `num` to `1 / 0`, `-1 / 0` and `0 / 0`
|
||||
* (each of type T) if `special` is `true`. This will fail on integer types
|
||||
* with a 'divide by zero'. Likewise, it will fail if `num` **is** one of
|
||||
* those special values, and `special` is `false`, because then the
|
||||
* algorithm just does normal calculations on them.
|
||||
*/
|
||||
pub pure fn to_str_bytes_common<T:NumCast+Zero+One+Eq+Ord+Round+Copy+Div<T,T>+
|
||||
Neg<T>+Modulo<T,T>+Mul<T,T>>(
|
||||
num: &T, radix: uint, special: bool, negative_zero: bool,
|
||||
sign: SignFormat, digits: SignificantDigits) -> (~[u8], bool) {
|
||||
if radix as int < 2 {
|
||||
fail!(fmt!("to_str_bytes_common: radix %? to low, \
|
||||
must lie in the range [2, 36]", radix));
|
||||
} else if radix as int > 36 {
|
||||
fail!(fmt!("to_str_bytes_common: radix %? to high, \
|
||||
must lie in the range [2, 36]", radix));
|
||||
}
|
||||
|
||||
let _0: T = Zero::zero();
|
||||
let _1: T = One::one();
|
||||
|
||||
if special {
|
||||
if is_NaN(num) {
|
||||
return (str::to_bytes("NaN"), true);
|
||||
} else if is_infinity(num){
|
||||
return match sign {
|
||||
SignAll => (str::to_bytes("+inf"), true),
|
||||
_ => (str::to_bytes("inf"), true)
|
||||
}
|
||||
} else if is_neg_infinity(num) {
|
||||
return match sign {
|
||||
SignNone => (str::to_bytes("inf"), true),
|
||||
_ => (str::to_bytes("-inf"), true),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let neg = *num < _0 || (negative_zero && *num == _0
|
||||
&& special && is_neg_zero(num));
|
||||
let mut buf: ~[u8] = ~[];
|
||||
let radix_gen: T = cast(radix as int);
|
||||
|
||||
let mut deccum;
|
||||
|
||||
// First emit the non-fractional part, looping at least once to make
|
||||
// sure at least a `0` gets emitted.
|
||||
deccum = num.round(RoundToZero);
|
||||
loop {
|
||||
// Calculate the absolute value of each digit instead of only
|
||||
// doing it once for the whole number because a
|
||||
// representable negative number doesn't necessary have an
|
||||
// representable additive inverse of the same type
|
||||
// (See twos complement). But we assume that for the
|
||||
// numbers [-35 .. 0] we always have [0 .. 35].
|
||||
let current_digit_signed = deccum % radix_gen;
|
||||
let current_digit = if current_digit_signed < _0 {
|
||||
-current_digit_signed
|
||||
} else {
|
||||
current_digit_signed
|
||||
};
|
||||
|
||||
// Decrease the deccumulator one digit at a time
|
||||
deccum /= radix_gen;
|
||||
deccum = deccum.round(RoundToZero);
|
||||
|
||||
unsafe { // FIXME: Pureness workaround (#4568)
|
||||
buf.push(char::from_digit(current_digit.to_int() as uint, radix)
|
||||
.unwrap() as u8);
|
||||
}
|
||||
|
||||
// No more digits to calculate for the non-fractional part -> break
|
||||
if deccum == _0 { break; }
|
||||
}
|
||||
|
||||
// If limited digits, calculate one digit more for rounding.
|
||||
let (limit_digits, digit_count, exact) = match digits {
|
||||
DigAll => (false, 0u, false),
|
||||
DigMax(count) => (true, count+1, false),
|
||||
DigExact(count) => (true, count+1, true)
|
||||
};
|
||||
|
||||
// Decide what sign to put in front
|
||||
match sign {
|
||||
SignNeg | SignAll if neg => {
|
||||
unsafe { // FIXME: Pureness workaround (#4568)
|
||||
buf.push('-' as u8);
|
||||
}
|
||||
}
|
||||
SignAll => {
|
||||
unsafe { // FIXME: Pureness workaround (#4568)
|
||||
buf.push('+' as u8);
|
||||
}
|
||||
}
|
||||
_ => ()
|
||||
}
|
||||
|
||||
unsafe { // FIXME: Pureness workaround (#4568)
|
||||
vec::reverse(buf);
|
||||
}
|
||||
|
||||
// Remember start of the fractional digits.
|
||||
// Points one beyond end of buf if none get generated,
|
||||
// or at the '.' otherwise.
|
||||
let start_fractional_digits = buf.len();
|
||||
|
||||
// Now emit the fractional part, if any
|
||||
deccum = num.fract();
|
||||
if deccum != _0 || (limit_digits && exact && digit_count > 0) {
|
||||
unsafe { // FIXME: Pureness workaround (#4568)
|
||||
buf.push('.' as u8);
|
||||
}
|
||||
let mut dig = 0u;
|
||||
|
||||
// calculate new digits while
|
||||
// - there is no limit and there are digits left
|
||||
// - or there is a limit, it's not reached yet and
|
||||
// - it's exact
|
||||
// - or it's a maximum, and there are still digits left
|
||||
while (!limit_digits && deccum != _0)
|
||||
|| (limit_digits && dig < digit_count && (
|
||||
exact
|
||||
|| (!exact && deccum != _0)
|
||||
)
|
||||
) {
|
||||
// Shift first fractional digit into the integer part
|
||||
deccum *= radix_gen;
|
||||
|
||||
// Calculate the absolute value of each digit.
|
||||
// See note in first loop.
|
||||
let current_digit_signed = deccum.round(RoundToZero);
|
||||
let current_digit = if current_digit_signed < _0 {
|
||||
-current_digit_signed
|
||||
} else {
|
||||
current_digit_signed
|
||||
};
|
||||
|
||||
unsafe { // FIXME: Pureness workaround (#4568)
|
||||
buf.push(char::from_digit(
|
||||
current_digit.to_int() as uint, radix).unwrap() as u8);
|
||||
}
|
||||
|
||||
// Decrease the deccumulator one fractional digit at a time
|
||||
deccum = deccum.fract();
|
||||
dig += 1u;
|
||||
}
|
||||
|
||||
// If digits are limited, and that limit has been reached,
|
||||
// cut off the one extra digit, and depending on its value
|
||||
// round the remaining ones.
|
||||
if limit_digits && dig == digit_count {
|
||||
let ascii2value = |chr: u8| {
|
||||
char::to_digit(chr as char, radix).unwrap() as uint
|
||||
};
|
||||
let value2ascii = |val: uint| {
|
||||
char::from_digit(val, radix).unwrap() as u8
|
||||
};
|
||||
|
||||
unsafe { // FIXME: Pureness workaround (#4568)
|
||||
let extra_digit = ascii2value(buf.pop());
|
||||
if extra_digit >= radix / 2 { // -> need to round
|
||||
let mut i: int = buf.len() as int - 1;
|
||||
loop {
|
||||
// If reached left end of number, have to
|
||||
// insert additional digit:
|
||||
if i < 0
|
||||
|| buf[i] == '-' as u8
|
||||
|| buf[i] == '+' as u8 {
|
||||
buf.insert((i + 1) as uint, value2ascii(1));
|
||||
break;
|
||||
}
|
||||
|
||||
// Skip the '.'
|
||||
if buf[i] == '.' as u8 { i -= 1; loop; }
|
||||
|
||||
// Either increment the digit,
|
||||
// or set to 0 if max and carry the 1.
|
||||
let current_digit = ascii2value(buf[i]);
|
||||
if current_digit < (radix - 1) {
|
||||
buf[i] = value2ascii(current_digit+1);
|
||||
break;
|
||||
} else {
|
||||
buf[i] = value2ascii(0);
|
||||
i -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if number of digits is not exact, remove all trailing '0's up to
|
||||
// and including the '.'
|
||||
if !exact {
|
||||
let buf_max_i = buf.len() - 1;
|
||||
|
||||
// index to truncate from
|
||||
let mut i = buf_max_i;
|
||||
|
||||
// discover trailing zeros of fractional part
|
||||
while i > start_fractional_digits && buf[i] == '0' as u8 {
|
||||
i -= 1;
|
||||
}
|
||||
|
||||
// Only attempt to truncate digits if buf has fractional digits
|
||||
if i >= start_fractional_digits {
|
||||
// If buf ends with '.', cut that too.
|
||||
if buf[i] == '.' as u8 { i -= 1 }
|
||||
|
||||
// only resize buf if we actually remove digits
|
||||
if i < buf_max_i {
|
||||
buf = buf.slice(0, i + 1);
|
||||
}
|
||||
}
|
||||
} // If exact and trailing '.', just cut that
|
||||
else {
|
||||
let max_i = buf.len() - 1;
|
||||
if buf[max_i] == '.' as u8 {
|
||||
buf = buf.slice(0, max_i);
|
||||
}
|
||||
}
|
||||
|
||||
(buf, false)
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a number to its string representation. This is a wrapper for
|
||||
* `to_str_bytes_common()`, for details see there.
|
||||
*/
|
||||
#[inline(always)]
|
||||
pub pure fn to_str_common<T:NumCast+Zero+One+Eq+Ord+Round+Copy+Div<T,T>+Neg<T>
|
||||
+Modulo<T,T>+Mul<T,T>>(
|
||||
num: &T, radix: uint, special: bool, negative_zero: bool,
|
||||
sign: SignFormat, digits: SignificantDigits) -> (~str, bool) {
|
||||
let (bytes, special) = to_str_bytes_common(num, radix, special,
|
||||
negative_zero, sign, digits);
|
||||
(str::from_bytes(bytes), special)
|
||||
}
|
||||
|
||||
// Some constants for from_str_bytes_common's input validation,
|
||||
// they define minimum radix values for which the character is a valid digit.
|
||||
priv const DIGIT_P_RADIX: uint = ('p' as uint) - ('a' as uint) + 11u;
|
||||
priv const DIGIT_I_RADIX: uint = ('i' as uint) - ('a' as uint) + 11u;
|
||||
priv const DIGIT_E_RADIX: uint = ('e' as uint) - ('a' as uint) + 11u;
|
||||
|
||||
/**
|
||||
* Parses a byte slice as a number. This is meant to
|
||||
* be a common base implementation for all numeric string conversion
|
||||
* functions like `from_str()` or `from_str_radix()`.
|
||||
*
|
||||
* # Arguments
|
||||
* - `buf` - The byte slice to parse.
|
||||
* - `radix` - Which base to parse the number as. Accepts 2-36.
|
||||
* - `negative` - Whether to accept negative numbers.
|
||||
* - `fractional` - Whether to accept numbers with fractional parts.
|
||||
* - `special` - Whether to accept special values like `inf`
|
||||
* and `NaN`. Can conflict with `radix`, see Failure.
|
||||
* - `exponent` - Which exponent format to accept. Options are:
|
||||
* - `ExpNone`: No Exponent, accepts just plain numbers like `42` or
|
||||
* `-8.2`.
|
||||
* - `ExpDec`: Accepts numbers with a decimal exponent like `42e5` or
|
||||
* `8.2E-2`. The exponent string itself is always base 10.
|
||||
* Can conflict with `radix`, see Failure.
|
||||
* - `ExpBin`: Accepts numbers with a binary exponent like `42P-8` or
|
||||
* `FFp128`. The exponent string itself is always base 10.
|
||||
* Can conflict with `radix`, see Failure.
|
||||
* - `empty_zero` - Whether to accept a empty `buf` as a 0 or not.
|
||||
*
|
||||
* # Return value
|
||||
* Returns `Some(n)` if `buf` parses to a number n without overflowing, and
|
||||
* `None` otherwise, depending on the constraints set by the remaining
|
||||
* arguments.
|
||||
*
|
||||
* # Failure
|
||||
* - Fails if `radix` < 2 or `radix` > 36.
|
||||
* - Fails if `radix` > 14 and `exponent` is `ExpDec` due to conflict
|
||||
* between digit and exponent sign `'e'`.
|
||||
* - Fails if `radix` > 25 and `exponent` is `ExpBin` due to conflict
|
||||
* between digit and exponent sign `'p'`.
|
||||
* - Fails if `radix` > 18 and `special == true` due to conflict
|
||||
* between digit and lowest first character in `inf` and `NaN`, the `'i'`.
|
||||
*
|
||||
* # Possible improvements
|
||||
* - Could accept option to allow ignoring underscores, allowing for numbers
|
||||
* formated like `FF_AE_FF_FF`.
|
||||
*/
|
||||
pub pure fn from_str_bytes_common<T:NumCast+Zero+One+Ord+Copy+Div<T,T>+
|
||||
Mul<T,T>+Sub<T,T>+Neg<T>+Add<T,T>>(
|
||||
buf: &[u8], radix: uint, negative: bool, fractional: bool,
|
||||
special: bool, exponent: ExponentFormat, empty_zero: bool
|
||||
) -> Option<T> {
|
||||
match exponent {
|
||||
ExpDec if radix >= DIGIT_E_RADIX // decimal exponent 'e'
|
||||
=> fail!(fmt!("from_str_bytes_common: radix %? incompatible with \
|
||||
use of 'e' as decimal exponent", radix)),
|
||||
ExpBin if radix >= DIGIT_P_RADIX // binary exponent 'p'
|
||||
=> fail!(fmt!("from_str_bytes_common: radix %? incompatible with \
|
||||
use of 'p' as binary exponent", radix)),
|
||||
_ if special && radix >= DIGIT_I_RADIX // first digit of 'inf'
|
||||
=> fail!(fmt!("from_str_bytes_common: radix %? incompatible with \
|
||||
special values 'inf' and 'NaN'", radix)),
|
||||
_ if radix as int < 2
|
||||
=> fail!(fmt!("from_str_bytes_common: radix %? to low, \
|
||||
must lie in the range [2, 36]", radix)),
|
||||
_ if radix as int > 36
|
||||
=> fail!(fmt!("from_str_bytes_common: radix %? to high, \
|
||||
must lie in the range [2, 36]", radix)),
|
||||
_ => ()
|
||||
}
|
||||
|
||||
let _0: T = Zero::zero();
|
||||
let _1: T = One::one();
|
||||
let radix_gen: T = cast(radix as int);
|
||||
|
||||
let len = buf.len();
|
||||
|
||||
if len == 0 {
|
||||
if empty_zero {
|
||||
return Some(_0);
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
if special {
|
||||
if buf == str::to_bytes("inf") || buf == str::to_bytes("+inf") {
|
||||
return Some(infinity());
|
||||
} else if buf == str::to_bytes("-inf") {
|
||||
if negative {
|
||||
return Some(neg_infinity());
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
} else if buf == str::to_bytes("NaN") {
|
||||
return Some(NaN());
|
||||
}
|
||||
}
|
||||
|
||||
let (start, accum_positive) = match buf[0] {
|
||||
'-' as u8 if !negative => return None,
|
||||
'-' as u8 => (1u, false),
|
||||
'+' as u8 => (1u, true),
|
||||
_ => (0u, true)
|
||||
};
|
||||
|
||||
// Initialize accumulator with signed zero for floating point parsing to
|
||||
// work
|
||||
let mut accum = if accum_positive { _0 } else { -_1 * _0};
|
||||
let mut last_accum = accum; // Necessary to detect overflow
|
||||
let mut i = start;
|
||||
let mut exp_found = false;
|
||||
|
||||
// Parse integer part of number
|
||||
while i < len {
|
||||
let c = buf[i] as char;
|
||||
|
||||
match char::to_digit(c, radix) {
|
||||
Some(digit) => {
|
||||
// shift accum one digit left
|
||||
accum *= radix_gen;
|
||||
|
||||
// add/subtract current digit depending on sign
|
||||
if accum_positive {
|
||||
accum += cast(digit as int);
|
||||
} else {
|
||||
accum -= cast(digit as int);
|
||||
}
|
||||
|
||||
// Detect overflow by comparing to last value
|
||||
if accum_positive && accum < last_accum { return None; }
|
||||
if !accum_positive && accum > last_accum { return None; }
|
||||
last_accum = accum;
|
||||
}
|
||||
None => match c {
|
||||
'e' | 'E' | 'p' | 'P' => {
|
||||
exp_found = true;
|
||||
break; // start of exponent
|
||||
}
|
||||
'.' if fractional => {
|
||||
i += 1u; // skip the '.'
|
||||
break; // start of fractional part
|
||||
}
|
||||
_ => return None // invalid number
|
||||
}
|
||||
}
|
||||
|
||||
i += 1u;
|
||||
}
|
||||
|
||||
// Parse fractional part of number
|
||||
// Skip if already reached start of exponent
|
||||
if !exp_found {
|
||||
let mut power = _1;
|
||||
|
||||
while i < len {
|
||||
let c = buf[i] as char;
|
||||
|
||||
match char::to_digit(c, radix) {
|
||||
Some(digit) => {
|
||||
// Decrease power one order of magnitude
|
||||
power /= radix_gen;
|
||||
|
||||
let digit_t: T = cast(digit);
|
||||
|
||||
// add/subtract current digit depending on sign
|
||||
if accum_positive {
|
||||
accum += digit_t * power;
|
||||
} else {
|
||||
accum -= digit_t * power;
|
||||
}
|
||||
|
||||
// Detect overflow by comparing to last value
|
||||
if accum_positive && accum < last_accum { return None; }
|
||||
if !accum_positive && accum > last_accum { return None; }
|
||||
last_accum = accum;
|
||||
}
|
||||
None => match c {
|
||||
'e' | 'E' | 'p' | 'P' => {
|
||||
exp_found = true;
|
||||
break; // start of exponent
|
||||
}
|
||||
_ => return None // invalid number
|
||||
}
|
||||
}
|
||||
|
||||
i += 1u;
|
||||
}
|
||||
}
|
||||
|
||||
// Special case: buf not empty, but does not contain any digit in front
|
||||
// of the exponent sign -> number is empty string
|
||||
if i == start {
|
||||
if empty_zero {
|
||||
return Some(_0);
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
let mut multiplier = _1;
|
||||
|
||||
if exp_found {
|
||||
let c = buf[i] as char;
|
||||
let base = match (c, exponent) {
|
||||
('e', ExpDec) | ('E', ExpDec) => 10u,
|
||||
('p', ExpBin) | ('P', ExpBin) => 2u,
|
||||
_ => return None // char doesn't fit given exponent format
|
||||
};
|
||||
|
||||
// parse remaining bytes as decimal integer,
|
||||
// skipping the exponent char
|
||||
let exp: Option<int> = from_str_bytes_common(
|
||||
buf.view(i+1, len), 10, true, false, false, ExpNone, false);
|
||||
|
||||
match exp {
|
||||
Some(exp_pow) => {
|
||||
multiplier = if exp_pow < 0 {
|
||||
_1 / pow_with_uint::<T>(base, (-exp_pow.to_int()) as uint)
|
||||
} else {
|
||||
pow_with_uint::<T>(base, exp_pow.to_int() as uint)
|
||||
}
|
||||
}
|
||||
None => return None // invalid exponent -> invalid number
|
||||
}
|
||||
}
|
||||
|
||||
Some(accum * multiplier)
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a string as a number. This is a wrapper for
|
||||
* `from_str_bytes_common()`, for details see there.
|
||||
*/
|
||||
#[inline(always)]
|
||||
pub pure fn from_str_common<T:NumCast+Zero+One+Ord+Copy+Div<T,T>+Mul<T,T>+
|
||||
Sub<T,T>+Neg<T>+Add<T,T>>(
|
||||
buf: &str, radix: uint, negative: bool, fractional: bool,
|
||||
special: bool, exponent: ExponentFormat, empty_zero: bool
|
||||
) -> Option<T> {
|
||||
from_str_bytes_common(str::to_bytes(buf), radix, negative,
|
||||
fractional, special, exponent, empty_zero)
|
||||
}
|
||||
|
|
556
src/libcore/num/strconv.rs
Normal file
556
src/libcore/num/strconv.rs
Normal file
|
@ -0,0 +1,556 @@
|
|||
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use core::cmp::{Ord, Eq};
|
||||
use ops::{Add, Div, Modulo, Mul, Neg, Sub};
|
||||
use option::{None, Option, Some};
|
||||
use char;
|
||||
use str;
|
||||
use kinds::Copy;
|
||||
use vec;
|
||||
use num::*;
|
||||
|
||||
pub enum ExponentFormat {
|
||||
ExpNone,
|
||||
ExpDec,
|
||||
ExpBin
|
||||
}
|
||||
|
||||
pub enum SignificantDigits {
|
||||
DigAll,
|
||||
DigMax(uint),
|
||||
DigExact(uint)
|
||||
}
|
||||
|
||||
pub enum SignFormat {
|
||||
SignNone,
|
||||
SignNeg,
|
||||
SignAll
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a number to its string representation as a byte vector.
|
||||
* This is meant to be a common base implementation for all numeric string
|
||||
* conversion functions like `to_str()` or `to_str_radix()`.
|
||||
*
|
||||
* # Arguments
|
||||
* - `num` - The number to convert. Accepts any number that
|
||||
* implements the numeric traits.
|
||||
* - `radix` - Base to use. Accepts only the values 2-36.
|
||||
* - `special` - Whether to attempt to compare to special values like
|
||||
* `inf` or `NaN`. Also needed to detect negative 0.
|
||||
* Can fail if it doesn't match `num`s type
|
||||
* (see safety note).
|
||||
* - `negative_zero` - Whether to treat the special value `-0` as
|
||||
* `-0` or as `+0`.
|
||||
* - `sign` - How to emit the sign. Options are:
|
||||
* - `SignNone`: No sign at all. Basically emits `abs(num)`.
|
||||
* - `SignNeg`: Only `-` on negative values.
|
||||
* - `SignAll`: Both `+` on positive, and `-` on negative numbers.
|
||||
* - `digits` - The amount of digits to use for emitting the
|
||||
* fractional part, if any. Options are:
|
||||
* - `DigAll`: All calculatable digits. Beware of bignums or
|
||||
* fractions!
|
||||
* - `DigMax(uint)`: Maximum N digits, truncating any trailing zeros.
|
||||
* - `DigExact(uint)`: Exactly N digits.
|
||||
*
|
||||
* # Return value
|
||||
* A tuple containing the byte vector, and a boolean flag indicating
|
||||
* whether it represents a special value like `inf`, `-inf`, `NaN` or not.
|
||||
* It returns a tuple because there can be ambiguity between a special value
|
||||
* and a number representation at higher bases.
|
||||
*
|
||||
* # Failure
|
||||
* - Fails if `radix` < 2 or `radix` > 36.
|
||||
* - Fails on wrong value for `special` (see safety note).
|
||||
*
|
||||
* # Safety note
|
||||
* The function detects the special values `inf`, `-inf` and `NaN` by
|
||||
* dynamically comparing `num` to `1 / 0`, `-1 / 0` and `0 / 0`
|
||||
* (each of type T) if `special` is `true`. This will fail on integer types
|
||||
* with a 'divide by zero'. Likewise, it will fail if `num` **is** one of
|
||||
* those special values, and `special` is `false`, because then the
|
||||
* algorithm just does normal calculations on them.
|
||||
*/
|
||||
pub pure fn to_str_bytes_common<T:NumCast+Zero+One+Eq+Ord+Round+Copy+Div<T,T>+
|
||||
Neg<T>+Modulo<T,T>+Mul<T,T>>(
|
||||
num: &T, radix: uint, special: bool, negative_zero: bool,
|
||||
sign: SignFormat, digits: SignificantDigits) -> (~[u8], bool) {
|
||||
if radix as int < 2 {
|
||||
fail!(fmt!("to_str_bytes_common: radix %? to low, \
|
||||
must lie in the range [2, 36]", radix));
|
||||
} else if radix as int > 36 {
|
||||
fail!(fmt!("to_str_bytes_common: radix %? to high, \
|
||||
must lie in the range [2, 36]", radix));
|
||||
}
|
||||
|
||||
let _0: T = Zero::zero();
|
||||
let _1: T = One::one();
|
||||
|
||||
if special {
|
||||
if is_NaN(num) {
|
||||
return (str::to_bytes("NaN"), true);
|
||||
} else if is_infinity(num){
|
||||
return match sign {
|
||||
SignAll => (str::to_bytes("+inf"), true),
|
||||
_ => (str::to_bytes("inf"), true)
|
||||
}
|
||||
} else if is_neg_infinity(num) {
|
||||
return match sign {
|
||||
SignNone => (str::to_bytes("inf"), true),
|
||||
_ => (str::to_bytes("-inf"), true),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let neg = *num < _0 || (negative_zero && *num == _0
|
||||
&& special && is_neg_zero(num));
|
||||
let mut buf: ~[u8] = ~[];
|
||||
let radix_gen: T = cast(radix as int);
|
||||
|
||||
let mut deccum;
|
||||
|
||||
// First emit the non-fractional part, looping at least once to make
|
||||
// sure at least a `0` gets emitted.
|
||||
deccum = num.round(RoundToZero);
|
||||
loop {
|
||||
// Calculate the absolute value of each digit instead of only
|
||||
// doing it once for the whole number because a
|
||||
// representable negative number doesn't necessary have an
|
||||
// representable additive inverse of the same type
|
||||
// (See twos complement). But we assume that for the
|
||||
// numbers [-35 .. 0] we always have [0 .. 35].
|
||||
let current_digit_signed = deccum % radix_gen;
|
||||
let current_digit = if current_digit_signed < _0 {
|
||||
-current_digit_signed
|
||||
} else {
|
||||
current_digit_signed
|
||||
};
|
||||
|
||||
// Decrease the deccumulator one digit at a time
|
||||
deccum /= radix_gen;
|
||||
deccum = deccum.round(RoundToZero);
|
||||
|
||||
unsafe { // FIXME: Pureness workaround (#4568)
|
||||
buf.push(char::from_digit(current_digit.to_int() as uint, radix)
|
||||
.unwrap() as u8);
|
||||
}
|
||||
|
||||
// No more digits to calculate for the non-fractional part -> break
|
||||
if deccum == _0 { break; }
|
||||
}
|
||||
|
||||
// If limited digits, calculate one digit more for rounding.
|
||||
let (limit_digits, digit_count, exact) = match digits {
|
||||
DigAll => (false, 0u, false),
|
||||
DigMax(count) => (true, count+1, false),
|
||||
DigExact(count) => (true, count+1, true)
|
||||
};
|
||||
|
||||
// Decide what sign to put in front
|
||||
match sign {
|
||||
SignNeg | SignAll if neg => {
|
||||
unsafe { // FIXME: Pureness workaround (#4568)
|
||||
buf.push('-' as u8);
|
||||
}
|
||||
}
|
||||
SignAll => {
|
||||
unsafe { // FIXME: Pureness workaround (#4568)
|
||||
buf.push('+' as u8);
|
||||
}
|
||||
}
|
||||
_ => ()
|
||||
}
|
||||
|
||||
unsafe { // FIXME: Pureness workaround (#4568)
|
||||
vec::reverse(buf);
|
||||
}
|
||||
|
||||
// Remember start of the fractional digits.
|
||||
// Points one beyond end of buf if none get generated,
|
||||
// or at the '.' otherwise.
|
||||
let start_fractional_digits = buf.len();
|
||||
|
||||
// Now emit the fractional part, if any
|
||||
deccum = num.fract();
|
||||
if deccum != _0 || (limit_digits && exact && digit_count > 0) {
|
||||
unsafe { // FIXME: Pureness workaround (#4568)
|
||||
buf.push('.' as u8);
|
||||
}
|
||||
let mut dig = 0u;
|
||||
|
||||
// calculate new digits while
|
||||
// - there is no limit and there are digits left
|
||||
// - or there is a limit, it's not reached yet and
|
||||
// - it's exact
|
||||
// - or it's a maximum, and there are still digits left
|
||||
while (!limit_digits && deccum != _0)
|
||||
|| (limit_digits && dig < digit_count && (
|
||||
exact
|
||||
|| (!exact && deccum != _0)
|
||||
)
|
||||
) {
|
||||
// Shift first fractional digit into the integer part
|
||||
deccum *= radix_gen;
|
||||
|
||||
// Calculate the absolute value of each digit.
|
||||
// See note in first loop.
|
||||
let current_digit_signed = deccum.round(RoundToZero);
|
||||
let current_digit = if current_digit_signed < _0 {
|
||||
-current_digit_signed
|
||||
} else {
|
||||
current_digit_signed
|
||||
};
|
||||
|
||||
unsafe { // FIXME: Pureness workaround (#4568)
|
||||
buf.push(char::from_digit(
|
||||
current_digit.to_int() as uint, radix).unwrap() as u8);
|
||||
}
|
||||
|
||||
// Decrease the deccumulator one fractional digit at a time
|
||||
deccum = deccum.fract();
|
||||
dig += 1u;
|
||||
}
|
||||
|
||||
// If digits are limited, and that limit has been reached,
|
||||
// cut off the one extra digit, and depending on its value
|
||||
// round the remaining ones.
|
||||
if limit_digits && dig == digit_count {
|
||||
let ascii2value = |chr: u8| {
|
||||
char::to_digit(chr as char, radix).unwrap() as uint
|
||||
};
|
||||
let value2ascii = |val: uint| {
|
||||
char::from_digit(val, radix).unwrap() as u8
|
||||
};
|
||||
|
||||
unsafe { // FIXME: Pureness workaround (#4568)
|
||||
let extra_digit = ascii2value(buf.pop());
|
||||
if extra_digit >= radix / 2 { // -> need to round
|
||||
let mut i: int = buf.len() as int - 1;
|
||||
loop {
|
||||
// If reached left end of number, have to
|
||||
// insert additional digit:
|
||||
if i < 0
|
||||
|| buf[i] == '-' as u8
|
||||
|| buf[i] == '+' as u8 {
|
||||
buf.insert((i + 1) as uint, value2ascii(1));
|
||||
break;
|
||||
}
|
||||
|
||||
// Skip the '.'
|
||||
if buf[i] == '.' as u8 { i -= 1; loop; }
|
||||
|
||||
// Either increment the digit,
|
||||
// or set to 0 if max and carry the 1.
|
||||
let current_digit = ascii2value(buf[i]);
|
||||
if current_digit < (radix - 1) {
|
||||
buf[i] = value2ascii(current_digit+1);
|
||||
break;
|
||||
} else {
|
||||
buf[i] = value2ascii(0);
|
||||
i -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if number of digits is not exact, remove all trailing '0's up to
|
||||
// and including the '.'
|
||||
if !exact {
|
||||
let buf_max_i = buf.len() - 1;
|
||||
|
||||
// index to truncate from
|
||||
let mut i = buf_max_i;
|
||||
|
||||
// discover trailing zeros of fractional part
|
||||
while i > start_fractional_digits && buf[i] == '0' as u8 {
|
||||
i -= 1;
|
||||
}
|
||||
|
||||
// Only attempt to truncate digits if buf has fractional digits
|
||||
if i >= start_fractional_digits {
|
||||
// If buf ends with '.', cut that too.
|
||||
if buf[i] == '.' as u8 { i -= 1 }
|
||||
|
||||
// only resize buf if we actually remove digits
|
||||
if i < buf_max_i {
|
||||
buf = buf.slice(0, i + 1);
|
||||
}
|
||||
}
|
||||
} // If exact and trailing '.', just cut that
|
||||
else {
|
||||
let max_i = buf.len() - 1;
|
||||
if buf[max_i] == '.' as u8 {
|
||||
buf = buf.slice(0, max_i);
|
||||
}
|
||||
}
|
||||
|
||||
(buf, false)
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a number to its string representation. This is a wrapper for
|
||||
* `to_str_bytes_common()`, for details see there.
|
||||
*/
|
||||
#[inline(always)]
|
||||
pub pure fn to_str_common<T:NumCast+Zero+One+Eq+Ord+Round+Copy+Div<T,T>+Neg<T>
|
||||
+Modulo<T,T>+Mul<T,T>>(
|
||||
num: &T, radix: uint, special: bool, negative_zero: bool,
|
||||
sign: SignFormat, digits: SignificantDigits) -> (~str, bool) {
|
||||
let (bytes, special) = to_str_bytes_common(num, radix, special,
|
||||
negative_zero, sign, digits);
|
||||
(str::from_bytes(bytes), special)
|
||||
}
|
||||
|
||||
// Some constants for from_str_bytes_common's input validation,
|
||||
// they define minimum radix values for which the character is a valid digit.
|
||||
priv const DIGIT_P_RADIX: uint = ('p' as uint) - ('a' as uint) + 11u;
|
||||
priv const DIGIT_I_RADIX: uint = ('i' as uint) - ('a' as uint) + 11u;
|
||||
priv const DIGIT_E_RADIX: uint = ('e' as uint) - ('a' as uint) + 11u;
|
||||
|
||||
/**
|
||||
* Parses a byte slice as a number. This is meant to
|
||||
* be a common base implementation for all numeric string conversion
|
||||
* functions like `from_str()` or `from_str_radix()`.
|
||||
*
|
||||
* # Arguments
|
||||
* - `buf` - The byte slice to parse.
|
||||
* - `radix` - Which base to parse the number as. Accepts 2-36.
|
||||
* - `negative` - Whether to accept negative numbers.
|
||||
* - `fractional` - Whether to accept numbers with fractional parts.
|
||||
* - `special` - Whether to accept special values like `inf`
|
||||
* and `NaN`. Can conflict with `radix`, see Failure.
|
||||
* - `exponent` - Which exponent format to accept. Options are:
|
||||
* - `ExpNone`: No Exponent, accepts just plain numbers like `42` or
|
||||
* `-8.2`.
|
||||
* - `ExpDec`: Accepts numbers with a decimal exponent like `42e5` or
|
||||
* `8.2E-2`. The exponent string itself is always base 10.
|
||||
* Can conflict with `radix`, see Failure.
|
||||
* - `ExpBin`: Accepts numbers with a binary exponent like `42P-8` or
|
||||
* `FFp128`. The exponent string itself is always base 10.
|
||||
* Can conflict with `radix`, see Failure.
|
||||
* - `empty_zero` - Whether to accept a empty `buf` as a 0 or not.
|
||||
*
|
||||
* # Return value
|
||||
* Returns `Some(n)` if `buf` parses to a number n without overflowing, and
|
||||
* `None` otherwise, depending on the constraints set by the remaining
|
||||
* arguments.
|
||||
*
|
||||
* # Failure
|
||||
* - Fails if `radix` < 2 or `radix` > 36.
|
||||
* - Fails if `radix` > 14 and `exponent` is `ExpDec` due to conflict
|
||||
* between digit and exponent sign `'e'`.
|
||||
* - Fails if `radix` > 25 and `exponent` is `ExpBin` due to conflict
|
||||
* between digit and exponent sign `'p'`.
|
||||
* - Fails if `radix` > 18 and `special == true` due to conflict
|
||||
* between digit and lowest first character in `inf` and `NaN`, the `'i'`.
|
||||
*
|
||||
* # Possible improvements
|
||||
* - Could accept option to allow ignoring underscores, allowing for numbers
|
||||
* formated like `FF_AE_FF_FF`.
|
||||
*/
|
||||
pub pure fn from_str_bytes_common<T:NumCast+Zero+One+Ord+Copy+Div<T,T>+
|
||||
Mul<T,T>+Sub<T,T>+Neg<T>+Add<T,T>>(
|
||||
buf: &[u8], radix: uint, negative: bool, fractional: bool,
|
||||
special: bool, exponent: ExponentFormat, empty_zero: bool
|
||||
) -> Option<T> {
|
||||
match exponent {
|
||||
ExpDec if radix >= DIGIT_E_RADIX // decimal exponent 'e'
|
||||
=> fail!(fmt!("from_str_bytes_common: radix %? incompatible with \
|
||||
use of 'e' as decimal exponent", radix)),
|
||||
ExpBin if radix >= DIGIT_P_RADIX // binary exponent 'p'
|
||||
=> fail!(fmt!("from_str_bytes_common: radix %? incompatible with \
|
||||
use of 'p' as binary exponent", radix)),
|
||||
_ if special && radix >= DIGIT_I_RADIX // first digit of 'inf'
|
||||
=> fail!(fmt!("from_str_bytes_common: radix %? incompatible with \
|
||||
special values 'inf' and 'NaN'", radix)),
|
||||
_ if radix as int < 2
|
||||
=> fail!(fmt!("from_str_bytes_common: radix %? to low, \
|
||||
must lie in the range [2, 36]", radix)),
|
||||
_ if radix as int > 36
|
||||
=> fail!(fmt!("from_str_bytes_common: radix %? to high, \
|
||||
must lie in the range [2, 36]", radix)),
|
||||
_ => ()
|
||||
}
|
||||
|
||||
let _0: T = Zero::zero();
|
||||
let _1: T = One::one();
|
||||
let radix_gen: T = cast(radix as int);
|
||||
|
||||
let len = buf.len();
|
||||
|
||||
if len == 0 {
|
||||
if empty_zero {
|
||||
return Some(_0);
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
if special {
|
||||
if buf == str::to_bytes("inf") || buf == str::to_bytes("+inf") {
|
||||
return Some(infinity());
|
||||
} else if buf == str::to_bytes("-inf") {
|
||||
if negative {
|
||||
return Some(neg_infinity());
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
} else if buf == str::to_bytes("NaN") {
|
||||
return Some(NaN());
|
||||
}
|
||||
}
|
||||
|
||||
let (start, accum_positive) = match buf[0] {
|
||||
'-' as u8 if !negative => return None,
|
||||
'-' as u8 => (1u, false),
|
||||
'+' as u8 => (1u, true),
|
||||
_ => (0u, true)
|
||||
};
|
||||
|
||||
// Initialize accumulator with signed zero for floating point parsing to
|
||||
// work
|
||||
let mut accum = if accum_positive { _0 } else { -_1 * _0};
|
||||
let mut last_accum = accum; // Necessary to detect overflow
|
||||
let mut i = start;
|
||||
let mut exp_found = false;
|
||||
|
||||
// Parse integer part of number
|
||||
while i < len {
|
||||
let c = buf[i] as char;
|
||||
|
||||
match char::to_digit(c, radix) {
|
||||
Some(digit) => {
|
||||
// shift accum one digit left
|
||||
accum *= radix_gen;
|
||||
|
||||
// add/subtract current digit depending on sign
|
||||
if accum_positive {
|
||||
accum += cast(digit as int);
|
||||
} else {
|
||||
accum -= cast(digit as int);
|
||||
}
|
||||
|
||||
// Detect overflow by comparing to last value
|
||||
if accum_positive && accum < last_accum { return None; }
|
||||
if !accum_positive && accum > last_accum { return None; }
|
||||
last_accum = accum;
|
||||
}
|
||||
None => match c {
|
||||
'e' | 'E' | 'p' | 'P' => {
|
||||
exp_found = true;
|
||||
break; // start of exponent
|
||||
}
|
||||
'.' if fractional => {
|
||||
i += 1u; // skip the '.'
|
||||
break; // start of fractional part
|
||||
}
|
||||
_ => return None // invalid number
|
||||
}
|
||||
}
|
||||
|
||||
i += 1u;
|
||||
}
|
||||
|
||||
// Parse fractional part of number
|
||||
// Skip if already reached start of exponent
|
||||
if !exp_found {
|
||||
let mut power = _1;
|
||||
|
||||
while i < len {
|
||||
let c = buf[i] as char;
|
||||
|
||||
match char::to_digit(c, radix) {
|
||||
Some(digit) => {
|
||||
// Decrease power one order of magnitude
|
||||
power /= radix_gen;
|
||||
|
||||
let digit_t: T = cast(digit);
|
||||
|
||||
// add/subtract current digit depending on sign
|
||||
if accum_positive {
|
||||
accum += digit_t * power;
|
||||
} else {
|
||||
accum -= digit_t * power;
|
||||
}
|
||||
|
||||
// Detect overflow by comparing to last value
|
||||
if accum_positive && accum < last_accum { return None; }
|
||||
if !accum_positive && accum > last_accum { return None; }
|
||||
last_accum = accum;
|
||||
}
|
||||
None => match c {
|
||||
'e' | 'E' | 'p' | 'P' => {
|
||||
exp_found = true;
|
||||
break; // start of exponent
|
||||
}
|
||||
_ => return None // invalid number
|
||||
}
|
||||
}
|
||||
|
||||
i += 1u;
|
||||
}
|
||||
}
|
||||
|
||||
// Special case: buf not empty, but does not contain any digit in front
|
||||
// of the exponent sign -> number is empty string
|
||||
if i == start {
|
||||
if empty_zero {
|
||||
return Some(_0);
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
let mut multiplier = _1;
|
||||
|
||||
if exp_found {
|
||||
let c = buf[i] as char;
|
||||
let base = match (c, exponent) {
|
||||
('e', ExpDec) | ('E', ExpDec) => 10u,
|
||||
('p', ExpBin) | ('P', ExpBin) => 2u,
|
||||
_ => return None // char doesn't fit given exponent format
|
||||
};
|
||||
|
||||
// parse remaining bytes as decimal integer,
|
||||
// skipping the exponent char
|
||||
let exp: Option<int> = from_str_bytes_common(
|
||||
buf.view(i+1, len), 10, true, false, false, ExpNone, false);
|
||||
|
||||
match exp {
|
||||
Some(exp_pow) => {
|
||||
multiplier = if exp_pow < 0 {
|
||||
_1 / pow_with_uint::<T>(base, (-exp_pow.to_int()) as uint)
|
||||
} else {
|
||||
pow_with_uint::<T>(base, exp_pow.to_int() as uint)
|
||||
}
|
||||
}
|
||||
None => return None // invalid exponent -> invalid number
|
||||
}
|
||||
}
|
||||
|
||||
Some(accum * multiplier)
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a string as a number. This is a wrapper for
|
||||
* `from_str_bytes_common()`, for details see there.
|
||||
*/
|
||||
#[inline(always)]
|
||||
pub pure fn from_str_common<T:NumCast+Zero+One+Ord+Copy+Div<T,T>+Mul<T,T>+
|
||||
Sub<T,T>+Neg<T>+Add<T,T>>(
|
||||
buf: &str, radix: uint, negative: bool, fractional: bool,
|
||||
special: bool, exponent: ExponentFormat, empty_zero: bool
|
||||
) -> Option<T> {
|
||||
from_str_bytes_common(str::to_bytes(buf), radix, negative,
|
||||
fractional, special, exponent, empty_zero)
|
||||
}
|
|
@ -17,6 +17,7 @@ use cmp;
|
|||
use to_str::ToStr;
|
||||
use from_str::FromStr;
|
||||
use num::{ToStrRadix, FromStrRadix};
|
||||
use num::strconv;
|
||||
use num;
|
||||
use option::{None, Option, Some};
|
||||
use prelude::*;
|
||||
|
@ -182,22 +183,22 @@ impl ops::Neg<T> for T {
|
|||
/// Parse a string as a number in base 10.
|
||||
#[inline(always)]
|
||||
pub pure fn from_str(s: &str) -> Option<T> {
|
||||
num::from_str_common(s, 10u, false, false, false,
|
||||
num::ExpNone, false)
|
||||
strconv::from_str_common(s, 10u, false, false, false,
|
||||
strconv::ExpNone, false)
|
||||
}
|
||||
|
||||
/// Parse a string as a number in the given base.
|
||||
#[inline(always)]
|
||||
pub pure fn from_str_radix(s: &str, radix: uint) -> Option<T> {
|
||||
num::from_str_common(s, radix, false, false, false,
|
||||
num::ExpNone, false)
|
||||
strconv::from_str_common(s, radix, false, false, false,
|
||||
strconv::ExpNone, false)
|
||||
}
|
||||
|
||||
/// Parse a byte slice as a number in the given base.
|
||||
#[inline(always)]
|
||||
pub pure fn parse_bytes(buf: &[u8], radix: uint) -> Option<T> {
|
||||
num::from_str_bytes_common(buf, radix, false, false, false,
|
||||
num::ExpNone, false)
|
||||
strconv::from_str_bytes_common(buf, radix, false, false, false,
|
||||
strconv::ExpNone, false)
|
||||
}
|
||||
|
||||
impl FromStr for T {
|
||||
|
@ -219,24 +220,24 @@ impl FromStrRadix for T {
|
|||
/// Convert to a string as a byte slice in a given base.
|
||||
#[inline(always)]
|
||||
pub pure fn to_str_bytes<U>(n: T, radix: uint, f: fn(v: &[u8]) -> U) -> U {
|
||||
let (buf, _) = num::to_str_bytes_common(&n, radix, false, false,
|
||||
num::SignNeg, num::DigAll);
|
||||
let (buf, _) = strconv::to_str_bytes_common(&n, radix, false, false,
|
||||
strconv::SignNeg, strconv::DigAll);
|
||||
f(buf)
|
||||
}
|
||||
|
||||
/// Convert to a string in base 10.
|
||||
#[inline(always)]
|
||||
pub pure fn to_str(num: T) -> ~str {
|
||||
let (buf, _) = num::to_str_common(&num, 10u, false, false,
|
||||
num::SignNeg, num::DigAll);
|
||||
let (buf, _) = strconv::to_str_common(&num, 10u, false, false,
|
||||
strconv::SignNeg, strconv::DigAll);
|
||||
buf
|
||||
}
|
||||
|
||||
/// Convert to a string in a given base.
|
||||
#[inline(always)]
|
||||
pub pure fn to_str_radix(num: T, radix: uint) -> ~str {
|
||||
let (buf, _) = num::to_str_common(&num, radix, false, false,
|
||||
num::SignNeg, num::DigAll);
|
||||
let (buf, _) = strconv::to_str_common(&num, radix, false, false,
|
||||
strconv::SignNeg, strconv::DigAll);
|
||||
buf
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue