Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make Debug include the - in -0.0 #24379

Merged
merged 1 commit into from
Apr 15, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 29 additions & 91 deletions src/libcore/fmt/float.rs
Original file line number Diff line number Diff line change
@@ -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.
//
Expand All @@ -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};
Expand Down Expand Up @@ -46,67 +42,42 @@ 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<T: Float, U, F>(
num: T,
radix: u32,
negative_zero: bool,
sign: SignFormat,
digits: SignificantDigits,
exp_format: ExponentFormat,
exp_upper: bool,
f: F
) -> 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"),
Expand All @@ -119,41 +90,28 @@ pub fn float_to_str_bytes_common<T: Float, U, F>(
_ => {}
}

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::<f64, T>(10.0f64).unwrap()),
ExpNone => panic!("unreachable"),
};

(num / exp_base.powf(exp), cast::<T, i32>(exp).unwrap())
ExpDec if num != _0 => {
let exp = num.log10().floor();
(num / radix_f.powf(exp), cast::<T, i32>(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);
Expand All @@ -170,15 +128,6 @@ pub fn float_to_str_bytes_common<T: Float, U, F>(
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.
Expand All @@ -205,14 +154,11 @@ pub fn float_to_str_bytes_common<T: Float, U, F>(
)
) {
// 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;

Expand Down Expand Up @@ -301,12 +247,8 @@ pub fn float_to_str_bytes_common<T: Float, U, F>(

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> {
Expand All @@ -324,11 +266,7 @@ pub fn float_to_str_bytes_common<T: Float, U, F>(
}

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));
}
}

Expand Down
46 changes: 23 additions & 23 deletions src/libcore/fmt/mod.rs
Original file line number Diff line number Diff line change
@@ -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.
//
Expand All @@ -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;
Expand Down Expand Up @@ -904,33 +905,38 @@ impl<'a, T> Pointer for &'a mut T {
}
}

// Common code of floating point Debug and Display.
fn float_to_str_common<T: Float, 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),
};
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)
})
}
}
Expand All @@ -945,9 +951,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,
Expand All @@ -967,9 +970,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,
Expand Down
8 changes: 7 additions & 1 deletion src/test/run-pass/ifmt.rs
Original file line number Diff line number Diff line change
@@ -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.
//
Expand Down Expand Up @@ -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;
Expand Down