core: made the core formatter to use a new flt2dec.

As a side effect `core::fmt::float` is gone now. This also slightly
changes the float output, so this is:

[breaking-change]
This commit is contained in:
Kang Seonghoon 2015-04-20 01:36:55 +09:00
parent c82da7a54b
commit 5aa9f38285
2 changed files with 125 additions and 332 deletions

View File

@ -1,289 +0,0 @@
// Copyright 2013-2015 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.
pub use self::ExponentFormat::*;
pub use self::SignificantDigits::*;
use prelude::*;
use char;
use fmt;
use num::Float;
use num::FpCategory as Fp;
use ops::{Div, Rem, Mul};
use slice;
use str;
/// 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
}
/// 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.
DigMax(usize),
/// Precisely the given number of digits will be printed.
DigExact(usize)
}
#[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 }
/// 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.
///
/// # Arguments
///
/// - `num` - The number to convert (non-negative). Accepts any number that
/// 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.
/// - `f` - A closure to invoke with the string representing the
/// float.
///
/// # Panics
///
/// - Panics if `num` is negative.
pub fn float_to_str_bytes_common<T: MyFloat, U, F>(
num: T,
digits: SignificantDigits,
exp_format: ExponentFormat,
exp_upper: bool,
f: F
) -> U where
F: FnOnce(&str) -> U,
{
let _0: T = T::zero();
let _1: T = T::one();
let radix: u32 = 10;
let radix_f = T::from_u32(radix);
assert!(num.is_nan() || num >= _0, "float_to_str_bytes_common: number is negative");
match num.classify() {
Fp::Nan => return f("NaN"),
Fp::Infinite if num > _0 => {
return f("inf");
}
Fp::Infinite if num < _0 => {
return f("-inf");
}
_ => {}
}
// 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];
let mut end = 0;
let (num, exp) = match exp_format {
ExpDec if num != _0 => {
let exp = num.log10().floor();
(num / radix_f.powf(exp), exp.to_i32())
}
_ => (num, 0)
};
// 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 {
let current_digit = deccum % radix_f;
// Decrease the deccumulator one digit at a time
deccum = deccum / radix_f;
deccum = deccum.trunc();
let c = char::from_digit(current_digit.to_i32() as u32, 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 {
DigMax(count) => (true, count + 1, false),
DigExact(count) => (true, count + 1, true)
};
buf[..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] = b'.';
end += 1;
let mut dig = 0;
// 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_f;
let current_digit = deccum.trunc();
let c = char::from_digit(current_digit.to_i32() as u32, radix);
buf[end] = c.unwrap() as u8;
end += 1;
// Decrease the deccumulator one fractional digit at a time
deccum = deccum.fract();
dig += 1;
}
// 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| {
(chr as char).to_digit(radix).unwrap()
};
let value2ascii = |val: u32| {
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: isize = end as isize - 1;
loop {
// If reached left end of number, have to
// insert additional digit:
if i < 0
|| buf[i as usize] == b'-'
|| buf[i as usize] == b'+' {
for j in ((i + 1) as usize..end).rev() {
buf[j + 1] = buf[j];
}
buf[(i + 1) as usize] = value2ascii(1);
end += 1;
break;
}
// Skip the '.'
if buf[i as usize] == b'.' { i -= 1; continue; }
// Either increment the digit,
// or set to 0 if max and carry the 1.
let current_digit = ascii2value(buf[i as usize]);
if current_digit < (radix - 1) {
buf[i as usize] = value2ascii(current_digit+1);
break;
} else {
buf[i as usize] = 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] == b'0' {
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] == b'.' { 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] == b'.' {
end = max_i;
}
}
match exp_format {
ExpNone => {},
ExpDec => {
buf[end] = if exp_upper { b'E' } else { b'e' };
end += 1;
struct Filler<'a> {
buf: &'a mut [u8],
end: &'a mut usize,
}
impl<'a> fmt::Write for Filler<'a> {
fn write_str(&mut self, s: &str) -> fmt::Result {
slice::bytes::copy_memory(s.as_bytes(),
&mut self.buf[(*self.end)..]);
*self.end += s.len();
Ok(())
}
}
let mut filler = Filler { buf: &mut buf, end: &mut end };
let _ = fmt::write(&mut filler, format_args!("{:-}", exp));
}
}
f(unsafe { str::from_utf8_unchecked(&buf[..end]) })
}

View File

@ -17,9 +17,9 @@
use cell::{Cell, RefCell, Ref, RefMut, BorrowState};
use marker::PhantomData;
use mem;
use num::flt2dec;
use ops::Deref;
use result;
use num::Float;
use slice;
use str;
use self::rt::v1::Alignment;
@ -31,7 +31,6 @@
pub use self::builders::{DebugStruct, DebugTuple, DebugSet, DebugList, DebugMap};
mod num;
mod float;
mod builders;
#[unstable(feature = "core", reason = "internal to format_args!")]
@ -604,6 +603,83 @@ fn with_padding<F>(&mut self, padding: usize, default: Alignment,
Ok(())
}
/// Takes the formatted parts and applies the padding.
/// Assumes that the caller already has rendered the parts with required precision,
/// so that `self.precision` can be ignored.
fn pad_formatted_parts(&mut self, formatted: &flt2dec::Formatted) -> Result {
if let Some(mut width) = self.width {
// for the sign-aware zero padding, we render the sign first and
// behave as if we had no sign from the beginning.
let mut formatted = formatted.clone();
let mut align = self.align;
let old_fill = self.fill;
if self.flags & (1 << (FlagV1::SignAwareZeroPad as u32)) != 0 {
// a sign always goes first
let sign = unsafe { str::from_utf8_unchecked(formatted.sign) };
try!(self.buf.write_str(sign));
// remove the sign from the formatted parts
formatted.sign = b"";
width = if width < sign.len() { 0 } else { width - sign.len() };
align = Alignment::Right;
self.fill = '0';
}
// remaining parts go through the ordinary padding process.
let len = formatted.len();
let ret = if width <= len { // no padding
self.write_formatted_parts(&formatted)
} else {
self.with_padding(width - len, align, |f| {
f.write_formatted_parts(&formatted)
})
};
self.fill = old_fill;
ret
} else {
// this is the common case and we take a shortcut
self.write_formatted_parts(formatted)
}
}
fn write_formatted_parts(&mut self, formatted: &flt2dec::Formatted) -> Result {
fn write_bytes(buf: &mut Write, s: &[u8]) -> Result {
buf.write_str(unsafe { str::from_utf8_unchecked(s) })
}
if !formatted.sign.is_empty() {
try!(write_bytes(self.buf, formatted.sign));
}
for part in formatted.parts {
match *part {
flt2dec::Part::Zero(mut nzeroes) => {
const ZEROES: &'static str = // 64 zeroes
"0000000000000000000000000000000000000000000000000000000000000000";
while nzeroes > ZEROES.len() {
try!(self.buf.write_str(ZEROES));
nzeroes -= ZEROES.len();
}
if nzeroes > 0 {
try!(self.buf.write_str(&ZEROES[..nzeroes]));
}
}
flt2dec::Part::Num(mut v) => {
let mut s = [0; 5];
let len = part.len();
for c in s[..len].iter_mut().rev() {
*c = b'0' + (v % 10) as u8;
v /= 10;
}
try!(write_bytes(self.buf, &s[..len]));
}
flt2dec::Part::Copy(buf) => {
try!(write_bytes(self.buf, buf));
}
}
}
Ok(())
}
/// Writes some data to the underlying buffer contained within this
/// formatter.
#[stable(feature = "rust1", since = "1.0.0")]
@ -918,18 +994,50 @@ fn fmt(&self, f: &mut Formatter) -> Result {
}
// Common code of floating point Debug and Display.
fn float_to_str_common<T: float::MyFloat, F>(num: &T, precision: Option<usize>,
post: F) -> Result
where F : FnOnce(&str) -> Result {
let digits = match precision {
Some(i) => float::DigExact(i),
None => float::DigMax(6),
fn float_to_decimal_common<T>(fmt: &mut Formatter, num: &T, negative_zero: bool) -> Result
where T: flt2dec::DecodableFloat
{
let force_sign = fmt.flags & (1 << (FlagV1::SignPlus as u32)) != 0;
let sign = match (force_sign, negative_zero) {
(false, false) => flt2dec::Sign::Minus,
(false, true) => flt2dec::Sign::MinusRaw,
(true, false) => flt2dec::Sign::MinusPlus,
(true, true) => flt2dec::Sign::MinusPlusRaw,
};
float::float_to_str_bytes_common(num.abs(),
digits,
float::ExpNone,
false,
post)
let mut buf = [0; 1024]; // enough for f32 and f64
let mut parts = [flt2dec::Part::Zero(0); 16];
let formatted = if let Some(precision) = fmt.precision {
flt2dec::to_exact_fixed_str(flt2dec::strategy::grisu::format_exact, *num, sign,
precision, false, &mut buf, &mut parts)
} else {
flt2dec::to_shortest_str(flt2dec::strategy::grisu::format_shortest, *num, sign,
0, false, &mut buf, &mut parts)
};
fmt.pad_formatted_parts(&formatted)
}
// Common code of floating point LowerExp and UpperExp.
fn float_to_exponential_common<T>(fmt: &mut Formatter, num: &T, upper: bool) -> Result
where T: flt2dec::DecodableFloat
{
let force_sign = fmt.flags & (1 << (FlagV1::SignPlus as u32)) != 0;
let sign = match force_sign {
false => flt2dec::Sign::Minus,
true => flt2dec::Sign::MinusPlus,
};
let mut buf = [0; 1024]; // enough for f32 and f64
let mut parts = [flt2dec::Part::Zero(0); 16];
let formatted = if let Some(precision) = fmt.precision {
// 1 integral digit + `precision` fractional digits = `precision + 1` total digits
flt2dec::to_exact_exp_str(flt2dec::strategy::grisu::format_exact, *num, sign,
precision + 1, upper, &mut buf, &mut parts)
} else {
flt2dec::to_shortest_exp_str(flt2dec::strategy::grisu::format_shortest, *num, sign,
(0, 0), upper, &mut buf, &mut parts)
};
fmt.pad_formatted_parts(&formatted)
}
macro_rules! floating { ($ty:ident) => {
@ -937,54 +1045,28 @@ macro_rules! floating { ($ty:ident) => {
#[stable(feature = "rust1", since = "1.0.0")]
impl Debug for $ty {
fn fmt(&self, fmt: &mut Formatter) -> Result {
float_to_str_common(self, fmt.precision, |absolute| {
// is_positive() counts -0.0 as negative
fmt.pad_integral(self.is_nan() || self.is_positive(), "", absolute)
})
float_to_decimal_common(fmt, self, true)
}
}
#[stable(feature = "rust1", since = "1.0.0")]
impl Display for $ty {
fn fmt(&self, fmt: &mut Formatter) -> Result {
float_to_str_common(self, fmt.precision, |absolute| {
// simple comparison counts -0.0 as positive
fmt.pad_integral(self.is_nan() || *self >= 0.0, "", absolute)
})
float_to_decimal_common(fmt, self, false)
}
}
#[stable(feature = "rust1", since = "1.0.0")]
impl LowerExp for $ty {
fn fmt(&self, fmt: &mut Formatter) -> Result {
let digits = match fmt.precision {
Some(i) => float::DigExact(i),
None => float::DigMax(6),
};
float::float_to_str_bytes_common(self.abs(),
digits,
float::ExpDec,
false,
|bytes| {
fmt.pad_integral(self.is_nan() || *self >= 0.0, "", bytes)
})
float_to_exponential_common(fmt, self, false)
}
}
#[stable(feature = "rust1", since = "1.0.0")]
impl UpperExp for $ty {
fn fmt(&self, fmt: &mut Formatter) -> Result {
let digits = match fmt.precision {
Some(i) => float::DigExact(i),
None => float::DigMax(6),
};
float::float_to_str_bytes_common(self.abs(),
digits,
float::ExpDec,
true,
|bytes| {
fmt.pad_integral(self.is_nan() || *self >= 0.0, "", bytes)
})
float_to_exponential_common(fmt, self, true)
}
}
} }