Skip to content

Commit 219f61b

Browse files
author
Robin Kruppe
committed
Make Debug include the - in -0.0
1 parent a1e3c25 commit 219f61b

File tree

3 files changed

+59
-115
lines changed

3 files changed

+59
-115
lines changed

src/libcore/fmt/float.rs

Lines changed: 29 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
1+
// Copyright 2013-2015 The Rust Project Developers. See the COPYRIGHT
22
// file at the top-level directory of this distribution and at
33
// http://rust-lang.org/COPYRIGHT.
44
//
@@ -8,14 +8,10 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
#![allow(missing_docs)]
12-
1311
pub use self::ExponentFormat::*;
1412
pub use self::SignificantDigits::*;
15-
pub use self::SignFormat::*;
1613

17-
use char;
18-
use char::CharExt;
14+
use char::{self, CharExt};
1915
use fmt;
2016
use iter::Iterator;
2117
use num::{cast, Float, ToPrimitive};
@@ -46,67 +42,42 @@ pub enum SignificantDigits {
4642
DigExact(usize)
4743
}
4844

49-
/// How to emit the sign of a number.
50-
pub enum SignFormat {
51-
/// `-` will be printed for negative values, but no sign will be emitted
52-
/// for positive numbers.
53-
SignNeg
54-
}
55-
56-
const DIGIT_E_RADIX: u32 = ('e' as u32) - ('a' as u32) + 11;
57-
58-
/// Converts a number to its string representation as a byte vector.
59-
/// This is meant to be a common base implementation for all numeric string
60-
/// conversion functions like `to_string()` or `to_str_radix()`.
45+
/// Converts a float number to its string representation.
46+
/// This is meant to be a common base implementation for various formatting styles.
47+
/// The number is assumed to be non-negative, callers use `Formatter::pad_integral`
48+
/// to add the right sign, if any.
6149
///
6250
/// # Arguments
6351
///
64-
/// - `num` - The number to convert. Accepts any number that
52+
/// - `num` - The number to convert (non-negative). Accepts any number that
6553
/// implements the numeric traits.
66-
/// - `radix` - Base to use. Accepts only the values 2-36. If the exponential notation
67-
/// is used, then this base is only used for the significand. The exponent
68-
/// itself always printed using a base of 10.
69-
/// - `negative_zero` - Whether to treat the special value `-0` as
70-
/// `-0` or as `+0`.
71-
/// - `sign` - How to emit the sign. See `SignFormat`.
7254
/// - `digits` - The amount of digits to use for emitting the fractional
7355
/// part, if any. See `SignificantDigits`.
7456
/// - `exp_format` - Whether or not to use the exponential (scientific) notation.
7557
/// See `ExponentFormat`.
7658
/// - `exp_capital` - Whether or not to use a capital letter for the exponent sign, if
7759
/// exponential notation is desired.
78-
/// - `f` - A closure to invoke with the bytes representing the
60+
/// - `f` - A closure to invoke with the string representing the
7961
/// float.
8062
///
8163
/// # Panics
8264
///
83-
/// - Panics if `radix` < 2 or `radix` > 36.
84-
/// - Panics if `radix` > 14 and `exp_format` is `ExpDec` due to conflict
85-
/// between digit and exponent sign `'e'`.
86-
/// - Panics if `radix` > 25 and `exp_format` is `ExpBin` due to conflict
87-
/// between digit and exponent sign `'p'`.
65+
/// - Panics if `num` is negative.
8866
pub fn float_to_str_bytes_common<T: Float, U, F>(
8967
num: T,
90-
radix: u32,
91-
negative_zero: bool,
92-
sign: SignFormat,
9368
digits: SignificantDigits,
9469
exp_format: ExponentFormat,
9570
exp_upper: bool,
9671
f: F
9772
) -> U where
9873
F: FnOnce(&str) -> U,
9974
{
100-
assert!(2 <= radix && radix <= 36);
101-
match exp_format {
102-
ExpDec if radix >= DIGIT_E_RADIX // decimal exponent 'e'
103-
=> panic!("float_to_str_bytes_common: radix {} incompatible with \
104-
use of 'e' as decimal exponent", radix),
105-
_ => ()
106-
}
107-
10875
let _0: T = Float::zero();
10976
let _1: T = Float::one();
77+
let radix: u32 = 10;
78+
let radix_f: T = cast(radix).unwrap();
79+
80+
assert!(num.is_nan() || num >= _0, "float_to_str_bytes_common: number is negative");
11081

11182
match num.classify() {
11283
Fp::Nan => return f("NaN"),
@@ -119,41 +90,28 @@ pub fn float_to_str_bytes_common<T: Float, U, F>(
11990
_ => {}
12091
}
12192

122-
let neg = num < _0 || (negative_zero && _1 / num == Float::neg_infinity());
123-
// For an f64 the exponent is in the range of [-1022, 1023] for base 2, so
124-
// we may have up to that many digits. Give ourselves some extra wiggle room
125-
// otherwise as well.
126-
let mut buf = [0; 1536];
93+
// For an f64 the (decimal) exponent is roughly in the range of [-307, 308], so
94+
// we may have up to that many digits. We err on the side of caution and
95+
// add 50% extra wiggle room.
96+
let mut buf = [0; 462];
12797
let mut end = 0;
128-
let radix_gen: T = cast(radix as isize).unwrap();
12998

13099
let (num, exp) = match exp_format {
131-
ExpNone => (num, 0),
132-
ExpDec if num == _0 => (num, 0),
133-
ExpDec => {
134-
let (exp, exp_base) = match exp_format {
135-
ExpDec => (num.abs().log10().floor(), cast::<f64, T>(10.0f64).unwrap()),
136-
ExpNone => panic!("unreachable"),
137-
};
138-
139-
(num / exp_base.powf(exp), cast::<T, i32>(exp).unwrap())
100+
ExpDec if num != _0 => {
101+
let exp = num.log10().floor();
102+
(num / radix_f.powf(exp), cast::<T, i32>(exp).unwrap())
140103
}
104+
_ => (num, 0)
141105
};
142106

143107
// First emit the non-fractional part, looping at least once to make
144108
// sure at least a `0` gets emitted.
145109
let mut deccum = num.trunc();
146110
loop {
147-
// Calculate the absolute value of each digit instead of only
148-
// doing it once for the whole number because a
149-
// representable negative number doesn't necessary have an
150-
// representable additive inverse of the same type
151-
// (See twos complement). But we assume that for the
152-
// numbers [-35 .. 0] we always have [0 .. 35].
153-
let current_digit = (deccum % radix_gen).abs();
111+
let current_digit = deccum % radix_f;
154112

155113
// Decrease the deccumulator one digit at a time
156-
deccum = deccum / radix_gen;
114+
deccum = deccum / radix_f;
157115
deccum = deccum.trunc();
158116

159117
let c = char::from_digit(current_digit.to_isize().unwrap() as u32, radix);
@@ -170,15 +128,6 @@ pub fn float_to_str_bytes_common<T: Float, U, F>(
170128
DigExact(count) => (true, count + 1, true)
171129
};
172130

173-
// Decide what sign to put in front
174-
match sign {
175-
SignNeg if neg => {
176-
buf[end] = b'-';
177-
end += 1;
178-
}
179-
_ => ()
180-
}
181-
182131
buf[..end].reverse();
183132

184133
// Remember start of the fractional digits.
@@ -205,14 +154,11 @@ pub fn float_to_str_bytes_common<T: Float, U, F>(
205154
)
206155
) {
207156
// Shift first fractional digit into the integer part
208-
deccum = deccum * radix_gen;
157+
deccum = deccum * radix_f;
209158

210-
// Calculate the absolute value of each digit.
211-
// See note in first loop.
212-
let current_digit = deccum.trunc().abs();
159+
let current_digit = deccum.trunc();
213160

214-
let c = char::from_digit(current_digit.to_isize().unwrap() as u32,
215-
radix);
161+
let c = char::from_digit(current_digit.to_isize().unwrap() as u32, radix);
216162
buf[end] = c.unwrap() as u8;
217163
end += 1;
218164

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

302248
match exp_format {
303249
ExpNone => {},
304-
_ => {
305-
buf[end] = match exp_format {
306-
ExpDec if exp_upper => 'E',
307-
ExpDec if !exp_upper => 'e',
308-
_ => panic!("unreachable"),
309-
} as u8;
250+
ExpDec => {
251+
buf[end] = if exp_upper { b'E' } else { b'e' };
310252
end += 1;
311253

312254
struct Filler<'a> {
@@ -324,11 +266,7 @@ pub fn float_to_str_bytes_common<T: Float, U, F>(
324266
}
325267

326268
let mut filler = Filler { buf: &mut buf, end: &mut end };
327-
match sign {
328-
SignNeg => {
329-
let _ = fmt::write(&mut filler, format_args!("{:-}", exp));
330-
}
331-
}
269+
let _ = fmt::write(&mut filler, format_args!("{:-}", exp));
332270
}
333271
}
334272

src/libcore/fmt/mod.rs

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
1+
// Copyright 2013-2015 The Rust Project Developers. See the COPYRIGHT
22
// file at the top-level directory of this distribution and at
33
// http://rust-lang.org/COPYRIGHT.
44
//
@@ -18,6 +18,7 @@ use clone::Clone;
1818
use iter::Iterator;
1919
use marker::{Copy, PhantomData, Sized};
2020
use mem;
21+
use num::Float;
2122
use option::Option;
2223
use option::Option::{Some, None};
2324
use result::Result::Ok;
@@ -904,33 +905,38 @@ impl<'a, T> Pointer for &'a mut T {
904905
}
905906
}
906907

908+
// Common code of floating point Debug and Display.
909+
fn float_to_str_common<T: Float, F>(num: &T, precision: Option<usize>, post: F) -> Result
910+
where F : FnOnce(&str) -> Result {
911+
let digits = match precision {
912+
Some(i) => float::DigExact(i),
913+
None => float::DigMax(6),
914+
};
915+
float::float_to_str_bytes_common(num.abs(),
916+
digits,
917+
float::ExpNone,
918+
false,
919+
post)
920+
}
921+
907922
macro_rules! floating { ($ty:ident) => {
908923

909924
#[stable(feature = "rust1", since = "1.0.0")]
910925
impl Debug for $ty {
911926
fn fmt(&self, fmt: &mut Formatter) -> Result {
912-
Display::fmt(self, fmt)
927+
float_to_str_common(self, fmt.precision, |absolute| {
928+
// is_positive() counts -0.0 as negative
929+
fmt.pad_integral(self.is_nan() || self.is_positive(), "", absolute)
930+
})
913931
}
914932
}
915933

916934
#[stable(feature = "rust1", since = "1.0.0")]
917935
impl Display for $ty {
918936
fn fmt(&self, fmt: &mut Formatter) -> Result {
919-
use num::Float;
920-
921-
let digits = match fmt.precision {
922-
Some(i) => float::DigExact(i),
923-
None => float::DigMax(6),
924-
};
925-
float::float_to_str_bytes_common(self.abs(),
926-
10,
927-
true,
928-
float::SignNeg,
929-
digits,
930-
float::ExpNone,
931-
false,
932-
|bytes| {
933-
fmt.pad_integral(self.is_nan() || *self >= 0.0, "", bytes)
937+
float_to_str_common(self, fmt.precision, |absolute| {
938+
// simple comparison counts -0.0 as positive
939+
fmt.pad_integral(self.is_nan() || *self >= 0.0, "", absolute)
934940
})
935941
}
936942
}
@@ -945,9 +951,6 @@ macro_rules! floating { ($ty:ident) => {
945951
None => float::DigMax(6),
946952
};
947953
float::float_to_str_bytes_common(self.abs(),
948-
10,
949-
true,
950-
float::SignNeg,
951954
digits,
952955
float::ExpDec,
953956
false,
@@ -967,9 +970,6 @@ macro_rules! floating { ($ty:ident) => {
967970
None => float::DigMax(6),
968971
};
969972
float::float_to_str_bytes_common(self.abs(),
970-
10,
971-
true,
972-
float::SignNeg,
973973
digits,
974974
float::ExpDec,
975975
true,

src/test/run-pass/ifmt.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
1+
// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT
22
// file at the top-level directory of this distribution and at
33
// http://rust-lang.org/COPYRIGHT.
44
//
@@ -144,6 +144,12 @@ pub fn main() {
144144
t!(format!("{:+10.3e}", 1.2345e6f64), " +1.234e6");
145145
t!(format!("{:+10.3e}", -1.2345e6f64), " -1.234e6");
146146

147+
// Float edge cases
148+
t!(format!("{}", -0.0), "0");
149+
t!(format!("{:?}", -0.0), "-0");
150+
t!(format!("{:?}", 0.0), "0");
151+
152+
147153
// Test that pointers don't get truncated.
148154
{
149155
let val = usize::MAX;

0 commit comments

Comments
 (0)