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:
parent
c82da7a54b
commit
5aa9f38285
@ -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]) })
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
} }
|
||||
|
Loading…
Reference in New Issue
Block a user