diff --git a/src/libcore/fmt/float.rs b/src/libcore/fmt/float.rs index 5f19bc5be98cc..72c25c6804022 100644 --- a/src/libcore/fmt/float.rs +++ b/src/libcore/fmt/float.rs @@ -1,4 +1,4 @@ -// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT +// 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. // @@ -8,14 +8,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![allow(missing_docs)] - pub use self::ExponentFormat::*; pub use self::SignificantDigits::*; -pub use self::SignFormat::*; -use char; -use char::CharExt; +use char::{self, CharExt}; use fmt; use iter::Iterator; use num::{cast, Float, ToPrimitive}; @@ -46,50 +42,29 @@ pub enum SignificantDigits { DigExact(usize) } -/// How to emit the sign of a number. -pub enum SignFormat { - /// `-` will be printed for negative values, but no sign will be emitted - /// for positive numbers. - SignNeg -} - -const DIGIT_E_RADIX: u32 = ('e' as u32) - ('a' as u32) + 11; - -/// 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_string()` or `to_str_radix()`. +/// 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. Accepts any number that +/// - `num` - The number to convert (non-negative). 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 +/// - `f` - A closure to invoke with the string representing the /// float. /// /// # Panics /// -/// - Panics if `radix` < 2 or `radix` > 36. -/// - Panics if `radix` > 14 and `exp_format` is `ExpDec` due to conflict -/// between digit and exponent sign `'e'`. -/// - Panics if `radix` > 25 and `exp_format` is `ExpBin` due to conflict -/// between digit and exponent sign `'p'`. +/// - Panics if `num` is negative. pub fn float_to_str_bytes_common( num: T, - radix: u32, - negative_zero: bool, - sign: SignFormat, digits: SignificantDigits, exp_format: ExponentFormat, exp_upper: bool, @@ -97,16 +72,12 @@ pub fn float_to_str_bytes_common( ) -> U where F: FnOnce(&str) -> U, { - assert!(2 <= radix && radix <= 36); - match exp_format { - ExpDec if radix >= DIGIT_E_RADIX // decimal exponent 'e' - => panic!("float_to_str_bytes_common: radix {} incompatible with \ - use of 'e' as decimal exponent", radix), - _ => () - } - let _0: T = Float::zero(); let _1: T = Float::one(); + let radix: u32 = 10; + let radix_f: T = cast(radix).unwrap(); + + assert!(num.is_nan() || num >= _0, "float_to_str_bytes_common: number is negative"); match num.classify() { Fp::Nan => return f("NaN"), @@ -119,41 +90,28 @@ pub fn float_to_str_bytes_common( _ => {} } - 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 = [0; 1536]; + // 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 radix_gen: T = cast(radix as isize).unwrap(); let (num, exp) = match exp_format { - ExpNone => (num, 0), - ExpDec if num == _0 => (num, 0), - ExpDec => { - let (exp, exp_base) = match exp_format { - ExpDec => (num.abs().log10().floor(), cast::(10.0f64).unwrap()), - ExpNone => panic!("unreachable"), - }; - - (num / exp_base.powf(exp), cast::(exp).unwrap()) + ExpDec if num != _0 => { + let exp = num.log10().floor(); + (num / radix_f.powf(exp), cast::(exp).unwrap()) } + _ => (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 { - // 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(); + let current_digit = deccum % radix_f; // Decrease the deccumulator one digit at a time - deccum = deccum / radix_gen; + deccum = deccum / radix_f; deccum = deccum.trunc(); let c = char::from_digit(current_digit.to_isize().unwrap() as u32, radix); @@ -170,15 +128,6 @@ pub fn float_to_str_bytes_common( DigExact(count) => (true, count + 1, true) }; - // Decide what sign to put in front - match sign { - SignNeg if neg => { - buf[end] = b'-'; - end += 1; - } - _ => () - } - buf[..end].reverse(); // Remember start of the fractional digits. @@ -205,14 +154,11 @@ pub fn float_to_str_bytes_common( ) ) { // Shift first fractional digit into the integer part - deccum = deccum * radix_gen; + deccum = deccum * radix_f; - // Calculate the absolute value of each digit. - // See note in first loop. - let current_digit = deccum.trunc().abs(); + let current_digit = deccum.trunc(); - let c = char::from_digit(current_digit.to_isize().unwrap() as u32, - radix); + let c = char::from_digit(current_digit.to_isize().unwrap() as u32, radix); buf[end] = c.unwrap() as u8; end += 1; @@ -301,12 +247,8 @@ pub fn float_to_str_bytes_common( match exp_format { ExpNone => {}, - _ => { - buf[end] = match exp_format { - ExpDec if exp_upper => 'E', - ExpDec if !exp_upper => 'e', - _ => panic!("unreachable"), - } as u8; + ExpDec => { + buf[end] = if exp_upper { b'E' } else { b'e' }; end += 1; struct Filler<'a> { @@ -324,11 +266,7 @@ pub fn float_to_str_bytes_common( } let mut filler = Filler { buf: &mut buf, end: &mut end }; - match sign { - SignNeg => { - let _ = fmt::write(&mut filler, format_args!("{:-}", exp)); - } - } + let _ = fmt::write(&mut filler, format_args!("{:-}", exp)); } } diff --git a/src/libcore/fmt/mod.rs b/src/libcore/fmt/mod.rs index 1c70f9941f7a0..80c661b260cbe 100644 --- a/src/libcore/fmt/mod.rs +++ b/src/libcore/fmt/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT +// 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. // @@ -18,6 +18,7 @@ use clone::Clone; use iter::Iterator; use marker::{Copy, PhantomData, Sized}; use mem; +use num::Float; use option::Option; use option::Option::{Some, None}; use result::Result::Ok; @@ -910,33 +911,38 @@ impl<'a, T> Pointer for &'a mut T { } } +// Common code of floating point Debug and Display. +fn float_to_str_common(num: &T, precision: Option, post: F) -> Result + where F : FnOnce(&str) -> Result { + let digits = match precision { + Some(i) => float::DigExact(i), + None => float::DigMax(6), + }; + float::float_to_str_bytes_common(num.abs(), + digits, + float::ExpNone, + false, + post) +} + macro_rules! floating { ($ty:ident) => { #[stable(feature = "rust1", since = "1.0.0")] impl Debug for $ty { fn fmt(&self, fmt: &mut Formatter) -> Result { - Display::fmt(self, fmt) + 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) + }) } } #[stable(feature = "rust1", since = "1.0.0")] impl Display for $ty { fn fmt(&self, fmt: &mut Formatter) -> Result { - use num::Float; - - let digits = match fmt.precision { - Some(i) => float::DigExact(i), - None => float::DigMax(6), - }; - float::float_to_str_bytes_common(self.abs(), - 10, - true, - float::SignNeg, - digits, - float::ExpNone, - false, - |bytes| { - fmt.pad_integral(self.is_nan() || *self >= 0.0, "", bytes) + 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) }) } } @@ -951,9 +957,6 @@ macro_rules! floating { ($ty:ident) => { None => float::DigMax(6), }; float::float_to_str_bytes_common(self.abs(), - 10, - true, - float::SignNeg, digits, float::ExpDec, false, @@ -973,9 +976,6 @@ macro_rules! floating { ($ty:ident) => { None => float::DigMax(6), }; float::float_to_str_bytes_common(self.abs(), - 10, - true, - float::SignNeg, digits, float::ExpDec, true, diff --git a/src/test/run-pass/ifmt.rs b/src/test/run-pass/ifmt.rs index 3a7af09764419..ea9db9b1e1f8d 100644 --- a/src/test/run-pass/ifmt.rs +++ b/src/test/run-pass/ifmt.rs @@ -1,4 +1,4 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -144,6 +144,12 @@ pub fn main() { t!(format!("{:+10.3e}", 1.2345e6f64), " +1.234e6"); t!(format!("{:+10.3e}", -1.2345e6f64), " -1.234e6"); + // Float edge cases + t!(format!("{}", -0.0), "0"); + t!(format!("{:?}", -0.0), "-0"); + t!(format!("{:?}", 0.0), "0"); + + // Test that pointers don't get truncated. { let val = usize::MAX;