2015-04-12 18:07:15 +02:00
|
|
|
// Copyright 2013-2015 The Rust Project Developers. See the COPYRIGHT
|
2014-05-10 14:06:25 -07:00
|
|
|
// 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.
|
|
|
|
|
2014-11-06 00:05:53 -08:00
|
|
|
pub use self::ExponentFormat::*;
|
|
|
|
pub use self::SignificantDigits::*;
|
|
|
|
|
2015-04-17 15:32:42 -07:00
|
|
|
use prelude::*;
|
|
|
|
|
|
|
|
use char;
|
2014-05-10 14:06:25 -07:00
|
|
|
use fmt;
|
2015-04-17 15:32:42 -07:00
|
|
|
use num::Float;
|
2014-12-22 22:50:57 +01:00
|
|
|
use num::FpCategory as Fp;
|
2015-04-17 15:32:42 -07:00
|
|
|
use ops::{Div, Rem, Mul};
|
|
|
|
use slice;
|
|
|
|
use str;
|
2014-05-10 14:06:25 -07:00
|
|
|
|
|
|
|
/// 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.
|
2014-09-20 15:37:14 +02:00
|
|
|
ExpDec
|
2014-05-10 14:06:25 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/// The number of digits used for emitting the fractional part of a number, if
|
|
|
|
/// any.
|
|
|
|
pub enum SignificantDigits {
|
|
|
|
/// At most the given number of digits will be printed, truncating any
|
|
|
|
/// trailing zeroes.
|
2015-02-23 16:07:38 +13:00
|
|
|
DigMax(usize),
|
2014-05-10 14:06:25 -07:00
|
|
|
|
|
|
|
/// Precisely the given number of digits will be printed.
|
2015-02-23 16:07:38 +13:00
|
|
|
DigExact(usize)
|
2014-05-10 14:06:25 -07:00
|
|
|
}
|
|
|
|
|
2015-04-17 15:32:42 -07:00
|
|
|
#[doc(hidden)]
|
|
|
|
pub trait MyFloat: Float + PartialEq + PartialOrd + Div<Output=Self> +
|
|
|
|
Mul<Output=Self> + Rem<Output=Self> + Copy {
|
|
|
|
fn from_u32(u: u32) -> Self;
|
|
|
|
fn to_i32(&self) -> i32;
|
|
|
|
}
|
|
|
|
|
|
|
|
macro_rules! doit {
|
|
|
|
($($t:ident)*) => ($(impl MyFloat for $t {
|
|
|
|
fn from_u32(u: u32) -> $t { u as $t }
|
|
|
|
fn to_i32(&self) -> i32 { *self as i32 }
|
|
|
|
})*)
|
|
|
|
}
|
|
|
|
doit! { f32 f64 }
|
|
|
|
|
2015-04-12 18:07:15 +02:00
|
|
|
/// Converts a float number to its string representation.
|
|
|
|
/// This is meant to be a common base implementation for various formatting styles.
|
|
|
|
/// The number is assumed to be non-negative, callers use `Formatter::pad_integral`
|
|
|
|
/// to add the right sign, if any.
|
2014-11-24 20:06:06 -05:00
|
|
|
///
|
|
|
|
/// # Arguments
|
|
|
|
///
|
2015-04-12 18:07:15 +02:00
|
|
|
/// - `num` - The number to convert (non-negative). Accepts any number that
|
2014-11-24 20:06:06 -05:00
|
|
|
/// implements the numeric traits.
|
|
|
|
/// - `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.
|
2015-04-12 18:07:15 +02:00
|
|
|
/// - `f` - A closure to invoke with the string representing the
|
2014-11-24 20:06:06 -05:00
|
|
|
/// float.
|
|
|
|
///
|
|
|
|
/// # Panics
|
|
|
|
///
|
2015-04-12 18:07:15 +02:00
|
|
|
/// - Panics if `num` is negative.
|
2015-04-17 15:32:42 -07:00
|
|
|
pub fn float_to_str_bytes_common<T: MyFloat, U, F>(
|
2014-05-10 14:06:25 -07:00
|
|
|
num: T,
|
|
|
|
digits: SignificantDigits,
|
|
|
|
exp_format: ExponentFormat,
|
|
|
|
exp_upper: bool,
|
2014-12-06 12:05:38 -05:00
|
|
|
f: F
|
|
|
|
) -> U where
|
2014-12-12 10:59:41 -08:00
|
|
|
F: FnOnce(&str) -> U,
|
2014-12-06 12:05:38 -05:00
|
|
|
{
|
2015-04-17 15:32:42 -07:00
|
|
|
let _0: T = T::zero();
|
|
|
|
let _1: T = T::one();
|
2015-04-12 18:07:15 +02:00
|
|
|
let radix: u32 = 10;
|
2015-04-17 15:32:42 -07:00
|
|
|
let radix_f = T::from_u32(radix);
|
2015-04-12 18:07:15 +02:00
|
|
|
|
|
|
|
assert!(num.is_nan() || num >= _0, "float_to_str_bytes_common: number is negative");
|
2014-05-10 14:06:25 -07:00
|
|
|
|
|
|
|
match num.classify() {
|
2014-12-12 10:59:41 -08:00
|
|
|
Fp::Nan => return f("NaN"),
|
2014-12-22 22:50:57 +01:00
|
|
|
Fp::Infinite if num > _0 => {
|
2014-12-12 10:59:41 -08:00
|
|
|
return f("inf");
|
2014-05-10 14:06:25 -07:00
|
|
|
}
|
2014-12-22 22:50:57 +01:00
|
|
|
Fp::Infinite if num < _0 => {
|
2014-12-12 10:59:41 -08:00
|
|
|
return f("-inf");
|
2014-05-10 14:06:25 -07:00
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
|
2015-04-12 18:07:15 +02:00
|
|
|
// For an f64 the (decimal) exponent is roughly in the range of [-307, 308], so
|
|
|
|
// we may have up to that many digits. We err on the side of caution and
|
|
|
|
// add 50% extra wiggle room.
|
|
|
|
let mut buf = [0; 462];
|
2014-05-10 14:06:25 -07:00
|
|
|
let mut end = 0;
|
|
|
|
|
|
|
|
let (num, exp) = match exp_format {
|
2015-04-12 18:07:15 +02:00
|
|
|
ExpDec if num != _0 => {
|
|
|
|
let exp = num.log10().floor();
|
2015-04-17 15:32:42 -07:00
|
|
|
(num / radix_f.powf(exp), exp.to_i32())
|
2014-05-10 14:06:25 -07:00
|
|
|
}
|
2015-04-12 18:07:15 +02:00
|
|
|
_ => (num, 0)
|
2014-05-10 14:06:25 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
// 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 {
|
2015-04-12 18:07:15 +02:00
|
|
|
let current_digit = deccum % radix_f;
|
2014-05-10 14:06:25 -07:00
|
|
|
|
|
|
|
// Decrease the deccumulator one digit at a time
|
2015-04-12 18:07:15 +02:00
|
|
|
deccum = deccum / radix_f;
|
2014-05-10 14:06:25 -07:00
|
|
|
deccum = deccum.trunc();
|
|
|
|
|
2015-04-17 15:32:42 -07:00
|
|
|
let c = char::from_digit(current_digit.to_i32() as u32, radix);
|
2014-05-10 14:06:25 -07:00
|
|
|
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 {
|
2014-09-20 15:37:14 +02:00
|
|
|
DigMax(count) => (true, count + 1, false),
|
|
|
|
DigExact(count) => (true, count + 1, true)
|
2014-05-10 14:06:25 -07:00
|
|
|
};
|
|
|
|
|
2015-01-17 16:15:52 -08:00
|
|
|
buf[..end].reverse();
|
2014-05-10 14:06:25 -07:00
|
|
|
|
|
|
|
// 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) {
|
2014-08-06 02:30:17 -04:00
|
|
|
buf[end] = b'.';
|
2014-05-10 14:06:25 -07:00
|
|
|
end += 1;
|
2015-01-22 14:08:56 +00:00
|
|
|
let mut dig = 0;
|
2014-05-10 14:06:25 -07:00
|
|
|
|
|
|
|
// 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
|
2015-04-12 18:07:15 +02:00
|
|
|
deccum = deccum * radix_f;
|
2014-05-10 14:06:25 -07:00
|
|
|
|
2015-04-12 18:07:15 +02:00
|
|
|
let current_digit = deccum.trunc();
|
2014-05-10 14:06:25 -07:00
|
|
|
|
2015-04-17 15:32:42 -07:00
|
|
|
let c = char::from_digit(current_digit.to_i32() as u32, radix);
|
2014-05-10 14:06:25 -07:00
|
|
|
buf[end] = c.unwrap() as u8;
|
|
|
|
end += 1;
|
|
|
|
|
|
|
|
// Decrease the deccumulator one fractional digit at a time
|
|
|
|
deccum = deccum.fract();
|
2015-01-22 14:08:56 +00:00
|
|
|
dig += 1;
|
2014-05-10 14:06:25 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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 {
|
2015-02-01 12:44:15 -05:00
|
|
|
let ascii2value = |chr: u8| {
|
2014-10-13 13:03:42 -07:00
|
|
|
(chr as char).to_digit(radix).unwrap()
|
2014-05-10 14:06:25 -07:00
|
|
|
};
|
2015-02-15 00:10:19 +03:00
|
|
|
let value2ascii = |val: u32| {
|
2014-05-10 14:06:25 -07:00
|
|
|
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
|
2015-03-25 17:06:52 -07:00
|
|
|
let mut i: isize = end as isize - 1;
|
2014-05-10 14:06:25 -07:00
|
|
|
loop {
|
|
|
|
// If reached left end of number, have to
|
|
|
|
// insert additional digit:
|
|
|
|
if i < 0
|
2015-02-23 16:07:38 +13:00
|
|
|
|| buf[i as usize] == b'-'
|
|
|
|
|| buf[i as usize] == b'+' {
|
2015-04-10 13:10:48 +01:00
|
|
|
for j in ((i + 1) as usize..end).rev() {
|
2014-05-10 14:06:25 -07:00
|
|
|
buf[j + 1] = buf[j];
|
|
|
|
}
|
2015-02-23 16:07:38 +13:00
|
|
|
buf[(i + 1) as usize] = value2ascii(1);
|
2014-05-10 14:06:25 -07:00
|
|
|
end += 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Skip the '.'
|
2015-02-23 16:07:38 +13:00
|
|
|
if buf[i as usize] == b'.' { i -= 1; continue; }
|
2014-05-10 14:06:25 -07:00
|
|
|
|
|
|
|
// Either increment the digit,
|
|
|
|
// or set to 0 if max and carry the 1.
|
2015-02-23 16:07:38 +13:00
|
|
|
let current_digit = ascii2value(buf[i as usize]);
|
2014-05-10 14:06:25 -07:00
|
|
|
if current_digit < (radix - 1) {
|
2015-02-23 16:07:38 +13:00
|
|
|
buf[i as usize] = value2ascii(current_digit+1);
|
2014-05-10 14:06:25 -07:00
|
|
|
break;
|
|
|
|
} else {
|
2015-02-23 16:07:38 +13:00
|
|
|
buf[i as usize] = value2ascii(0);
|
2014-05-10 14:06:25 -07:00
|
|
|
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
|
2014-08-06 02:30:17 -04:00
|
|
|
while i > start_fractional_digits && buf[i] == b'0' {
|
2014-05-10 14:06:25 -07:00
|
|
|
i -= 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Only attempt to truncate digits if buf has fractional digits
|
|
|
|
if i >= start_fractional_digits {
|
|
|
|
// If buf ends with '.', cut that too.
|
2014-08-06 02:30:17 -04:00
|
|
|
if buf[i] == b'.' { i -= 1 }
|
2014-05-10 14:06:25 -07:00
|
|
|
|
|
|
|
// 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;
|
2014-08-06 02:30:17 -04:00
|
|
|
if buf[max_i] == b'.' {
|
2014-05-10 14:06:25 -07:00
|
|
|
end = max_i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
match exp_format {
|
|
|
|
ExpNone => {},
|
2015-04-12 18:07:15 +02:00
|
|
|
ExpDec => {
|
|
|
|
buf[end] = if exp_upper { b'E' } else { b'e' };
|
2014-05-10 14:06:25 -07:00
|
|
|
end += 1;
|
|
|
|
|
|
|
|
struct Filler<'a> {
|
|
|
|
buf: &'a mut [u8],
|
2015-02-23 16:07:38 +13:00
|
|
|
end: &'a mut usize,
|
2014-05-10 14:06:25 -07:00
|
|
|
}
|
|
|
|
|
2015-02-14 12:56:32 +13:00
|
|
|
impl<'a> fmt::Write for Filler<'a> {
|
2014-12-12 10:59:41 -08:00
|
|
|
fn write_str(&mut self, s: &str) -> fmt::Result {
|
2015-03-27 11:12:28 -07:00
|
|
|
slice::bytes::copy_memory(s.as_bytes(),
|
|
|
|
&mut self.buf[(*self.end)..]);
|
2014-12-12 10:59:41 -08:00
|
|
|
*self.end += s.len();
|
2014-05-10 14:06:25 -07:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-17 21:39:01 +13:00
|
|
|
let mut filler = Filler { buf: &mut buf, end: &mut end };
|
2015-04-12 18:07:15 +02:00
|
|
|
let _ = fmt::write(&mut filler, format_args!("{:-}", exp));
|
2014-05-10 14:06:25 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-12 16:59:18 -05:00
|
|
|
f(unsafe { str::from_utf8_unchecked(&buf[..end]) })
|
2014-05-10 14:06:25 -07:00
|
|
|
}
|