forked from rust-lang/rust
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This is a migration of the std::{f32, f64}::to_str* functionality to the core library. This removes the growable `Vec` used in favor of a large stack buffer. The maximum base 10 exponent for f64 is 308, so a stack buffer of 512 bytes should be sufficient to store all floats.
- Loading branch information
1 parent
1de4b65
commit bcab97a
Showing
1 changed file
with
362 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,362 @@ | ||
// Copyright 2013-2014 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. | ||
|
||
#![allow(missing_doc)] | ||
|
||
use char; | ||
use container::Container; | ||
use fmt; | ||
use iter::{Iterator, range, DoubleEndedIterator}; | ||
use num::{Float, FPNaN, FPInfinite, ToPrimitive, Primitive}; | ||
use num::{Zero, One, cast}; | ||
use option::{None, Some}; | ||
use result::Ok; | ||
use slice::{ImmutableVector, MutableVector}; | ||
use slice; | ||
use str::StrSlice; | ||
|
||
/// A flag that specifies whether to use exponential (scientific) notation. | ||
pub enum ExponentFormat { | ||
/// Do not use exponential notation. | ||
ExpNone, | ||
/// Use exponential notation with the exponent having a base of 10 and the | ||
/// exponent sign being `e` or `E`. For example, 1000 would be printed | ||
/// 1e3. | ||
ExpDec, | ||
/// Use exponential notation with the exponent having a base of 2 and the | ||
/// exponent sign being `p` or `P`. For example, 8 would be printed 1p3. | ||
ExpBin, | ||
} | ||
|
||
/// The number of digits used for emitting the fractional part of a number, if | ||
/// any. | ||
pub enum SignificantDigits { | ||
/// All calculable digits will be printed. | ||
/// | ||
/// Note that bignums or fractions may cause a surprisingly large number | ||
/// of digits to be printed. | ||
DigAll, | ||
|
||
/// At most the given number of digits will be printed, truncating any | ||
/// trailing zeroes. | ||
DigMax(uint), | ||
|
||
/// Precisely the given number of digits will be printed. | ||
DigExact(uint) | ||
} | ||
|
||
/// How to emit the sign of a number. | ||
pub enum SignFormat { | ||
/// No sign will be printed. The exponent sign will also be emitted. | ||
SignNone, | ||
/// `-` will be printed for negative values, but no sign will be emitted | ||
/// for positive numbers. | ||
SignNeg, | ||
/// `+` will be printed for positive values, and `-` will be printed for | ||
/// negative values. | ||
SignAll, | ||
} | ||
|
||
static DIGIT_P_RADIX: uint = ('p' as uint) - ('a' as uint) + 11u; | ||
static DIGIT_E_RADIX: uint = ('e' as uint) - ('a' as uint) + 11u; | ||
|
||
/** | ||
* 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. If the exponential notation | ||
* is used, then this base is only used for the significand. The exponent | ||
* itself always printed using a base of 10. | ||
* - `negative_zero` - Whether to treat the special value `-0` as | ||
* `-0` or as `+0`. | ||
* - `sign` - How to emit the sign. See `SignFormat`. | ||
* - `digits` - The amount of digits to use for emitting the fractional | ||
* part, if any. See `SignificantDigits`. | ||
* - `exp_format` - Whether or not to use the exponential (scientific) notation. | ||
* See `ExponentFormat`. | ||
* - `exp_capital` - Whether or not to use a capital letter for the exponent sign, if | ||
* exponential notation is desired. | ||
* - `f` - A closure to invoke with the bytes representing the | ||
* float. | ||
* | ||
* # Failure | ||
* - Fails if `radix` < 2 or `radix` > 36. | ||
* - Fails if `radix` > 14 and `exp_format` is `ExpDec` due to conflict | ||
* between digit and exponent sign `'e'`. | ||
* - Fails if `radix` > 25 and `exp_format` is `ExpBin` due to conflict | ||
* between digit and exponent sign `'p'`. | ||
*/ | ||
pub fn float_to_str_bytes_common<T: Primitive + Float, U>( | ||
num: T, | ||
radix: uint, | ||
negative_zero: bool, | ||
sign: SignFormat, | ||
digits: SignificantDigits, | ||
exp_format: ExponentFormat, | ||
exp_upper: bool, | ||
f: |&[u8]| -> U | ||
) -> U { | ||
assert!(2 <= radix && radix <= 36); | ||
match exp_format { | ||
ExpDec if radix >= DIGIT_E_RADIX // decimal exponent 'e' | ||
=> fail!("float_to_str_bytes_common: radix {} incompatible with \ | ||
use of 'e' as decimal exponent", radix), | ||
ExpBin if radix >= DIGIT_P_RADIX // binary exponent 'p' | ||
=> fail!("float_to_str_bytes_common: radix {} incompatible with \ | ||
use of 'p' as binary exponent", radix), | ||
_ => () | ||
} | ||
|
||
let _0: T = Zero::zero(); | ||
let _1: T = One::one(); | ||
|
||
match num.classify() { | ||
FPNaN => return f("NaN".as_bytes()), | ||
FPInfinite if num > _0 => { | ||
return match sign { | ||
SignAll => return f("+inf".as_bytes()), | ||
_ => return f("inf".as_bytes()), | ||
}; | ||
} | ||
FPInfinite if num < _0 => { | ||
return match sign { | ||
SignNone => return f("inf".as_bytes()), | ||
_ => return f("-inf".as_bytes()), | ||
}; | ||
} | ||
_ => {} | ||
} | ||
|
||
let neg = num < _0 || (negative_zero && _1 / num == Float::neg_infinity()); | ||
// For an f64 the exponent is in the range of [-1022, 1023] for base 2, so | ||
// we may have up to that many digits. Give ourselves some extra wiggle room | ||
// otherwise as well. | ||
let mut buf = [0u8, ..1536]; | ||
let mut end = 0; | ||
let radix_gen: T = cast(radix as int).unwrap(); | ||
|
||
let (num, exp) = match exp_format { | ||
ExpNone => (num, 0i32), | ||
ExpDec | ExpBin if num == _0 => (num, 0i32), | ||
ExpDec | ExpBin => { | ||
let (exp, exp_base) = match exp_format { | ||
ExpDec => (num.abs().log10().floor(), cast::<f64, T>(10.0f64).unwrap()), | ||
ExpBin => (num.abs().log2().floor(), cast::<f64, T>(2.0f64).unwrap()), | ||
ExpNone => fail!("unreachable"), | ||
}; | ||
|
||
(num / exp_base.powf(exp), cast::<T, i32>(exp).unwrap()) | ||
} | ||
}; | ||
|
||
// First emit the non-fractional part, looping at least once to make | ||
// sure at least a `0` gets emitted. | ||
let mut deccum = num.trunc(); | ||
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 = (deccum % radix_gen).abs(); | ||
|
||
// Decrease the deccumulator one digit at a time | ||
deccum = deccum / radix_gen; | ||
deccum = deccum.trunc(); | ||
|
||
let c = char::from_digit(current_digit.to_int().unwrap() as uint, radix); | ||
buf[end] = c.unwrap() as u8; | ||
end += 1; | ||
|
||
// 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 => { | ||
buf[end] = '-' as u8; | ||
end += 1; | ||
} | ||
SignAll => { | ||
buf[end] = '+' as u8; | ||
end += 1; | ||
} | ||
_ => () | ||
} | ||
|
||
buf.mut_slice_to(end).reverse(); | ||
|
||
// Remember start of the fractional digits. | ||
// Points one beyond end of buf if none get generated, | ||
// or at the '.' otherwise. | ||
let start_fractional_digits = end; | ||
|
||
// Now emit the fractional part, if any | ||
deccum = num.fract(); | ||
if deccum != _0 || (limit_digits && exact && digit_count > 0) { | ||
buf[end] = '.' as u8; | ||
end += 1; | ||
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 = deccum * radix_gen; | ||
|
||
// Calculate the absolute value of each digit. | ||
// See note in first loop. | ||
let current_digit = deccum.trunc().abs(); | ||
|
||
let c = char::from_digit(current_digit.to_int().unwrap() as uint, | ||
radix); | ||
buf[end] = c.unwrap() as u8; | ||
end += 1; | ||
|
||
// 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() | ||
}; | ||
let value2ascii = |val: uint| { | ||
char::from_digit(val, radix).unwrap() as u8 | ||
}; | ||
|
||
let extra_digit = ascii2value(buf[end - 1]); | ||
end -= 1; | ||
if extra_digit >= radix / 2 { // -> need to round | ||
let mut i: int = end as int - 1; | ||
loop { | ||
// If reached left end of number, have to | ||
// insert additional digit: | ||
if i < 0 | ||
|| buf[i as uint] == '-' as u8 | ||
|| buf[i as uint] == '+' as u8 { | ||
for j in range(i as uint + 1, end).rev() { | ||
buf[j + 1] = buf[j]; | ||
} | ||
buf[(i + 1) as uint] = value2ascii(1); | ||
end += 1; | ||
break; | ||
} | ||
|
||
// Skip the '.' | ||
if buf[i as uint] == '.' as u8 { i -= 1; continue; } | ||
|
||
// Either increment the digit, | ||
// or set to 0 if max and carry the 1. | ||
let current_digit = ascii2value(buf[i as uint]); | ||
if current_digit < (radix - 1) { | ||
buf[i as uint] = value2ascii(current_digit+1); | ||
break; | ||
} else { | ||
buf[i as uint] = 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 = end - 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 { | ||
end = i + 1; | ||
} | ||
} | ||
} // If exact and trailing '.', just cut that | ||
else { | ||
let max_i = end - 1; | ||
if buf[max_i] == '.' as u8 { | ||
end = max_i; | ||
} | ||
} | ||
|
||
match exp_format { | ||
ExpNone => {}, | ||
_ => { | ||
buf[end] = match exp_format { | ||
ExpDec if exp_upper => 'E', | ||
ExpDec if !exp_upper => 'e', | ||
ExpBin if exp_upper => 'P', | ||
ExpBin if !exp_upper => 'p', | ||
_ => fail!("unreachable"), | ||
} as u8; | ||
end += 1; | ||
|
||
struct Filler<'a> { | ||
buf: &'a mut [u8], | ||
end: &'a mut uint, | ||
} | ||
|
||
impl<'a> fmt::FormatWriter for Filler<'a> { | ||
fn write(&mut self, bytes: &[u8]) -> fmt::Result { | ||
slice::bytes::copy_memory(self.buf.mut_slice_from(*self.end), | ||
bytes); | ||
*self.end += bytes.len(); | ||
Ok(()) | ||
} | ||
} | ||
|
||
let mut filler = Filler { buf: buf, end: &mut end }; | ||
match sign { | ||
SignNeg => { let _ = write!(&mut filler, "{:-}", exp); } | ||
SignNone | SignAll => { let _ = write!(&mut filler, "{}", exp); } | ||
} | ||
} | ||
} | ||
|
||
f(buf.slice_to(end)) | ||
} |