diff options
| author | Yuki Okushi <jtitor@2k36.org> | 2021-10-20 04:35:10 +0900 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-10-20 04:35:10 +0900 |
| commit | ca6798ab073c4f2358f2577e7258108099f29144 (patch) | |
| tree | 32f4e71f7579abd2cc56f7b991f3a8c80e966336 | |
| parent | 1af55d19c7a9189374d89472f97dc119659bb67e (diff) | |
| parent | 8731d4dfb479914a91f650f4f124528e332e8128 (diff) | |
| download | rust-ca6798ab073c4f2358f2577e7258108099f29144.tar.gz rust-ca6798ab073c4f2358f2577e7258108099f29144.zip | |
Rollup merge of #86479 - exphp-forks:float-debug-exponential, r=yaahc
Automatic exponential formatting in Debug
Context: See [this comment from the libs team](https://github.com/rust-lang/rfcs/pull/2729#issuecomment-853454204)
---
Makes `"{:?}"` switch to exponential for floats based on magnitude. The libs team suggested exploring this idea in the discussion thread for RFC rust-lang/rfcs#2729. (**note:** this is **not** an implementation of the RFC; it is an implementation of one of the alternatives)
Thresholds chosen were 1e-4 and 1e16. Justification described [here](https://github.com/rust-lang/rfcs/pull/2729#issuecomment-864482954).
**This will require a crater run.**
---
As mentioned in the commit message of 8731d4dfb47, this behavior will not apply when a precision is supplied, because I wanted to preserve the following existing and useful behavior of `{:.PREC?}` (which recursively applies `{:.PREC}` to floats in a struct):
```rust
assert_eq!(
format!("{:.2?}", [100.0, 0.000004]),
"[100.00, 0.00]",
)
```
I looked around and am not sure where there are any tests that actually use this in the test suite, though?
All things considered, I'm surprised that this change did not seem to break even a single existing test in `x.py test --stage 2`. (even when I tried a smaller threshold of 1e6)
| -rw-r--r-- | library/core/src/fmt/float.rs | 53 | ||||
| -rw-r--r-- | library/core/src/num/f32.rs | 2 | ||||
| -rw-r--r-- | library/core/src/num/f64.rs | 2 | ||||
| -rw-r--r-- | library/core/tests/fmt/float.rs | 24 |
4 files changed, 75 insertions, 6 deletions
diff --git a/library/core/src/fmt/float.rs b/library/core/src/fmt/float.rs index ba65f0fadbd..89d5fac30d3 100644 --- a/library/core/src/fmt/float.rs +++ b/library/core/src/fmt/float.rs @@ -3,6 +3,26 @@ use crate::mem::MaybeUninit; use crate::num::flt2dec; use crate::num::fmt as numfmt; +#[doc(hidden)] +trait GeneralFormat: PartialOrd { + /// Determines if a value should use exponential based on its magnitude, given the precondition + /// that it will not be rounded any further before it is displayed. + fn already_rounded_value_should_use_exponential(&self) -> bool; +} + +macro_rules! impl_general_format { + ($($t:ident)*) => { + $(impl GeneralFormat for $t { + fn already_rounded_value_should_use_exponential(&self) -> bool { + let abs = $t::abs_private(*self); + (abs != 0.0 && abs < 1e-4) || abs >= 1e+16 + } + })* + } +} + +impl_general_format! { f32 f64 } + // Don't inline this so callers don't use the stack space this function // requires unless they have to. #[inline(never)] @@ -54,8 +74,7 @@ where fmt.pad_formatted_parts(&formatted) } -// Common code of floating point Debug and Display. -fn float_to_decimal_common<T>(fmt: &mut Formatter<'_>, num: &T, min_precision: usize) -> Result +fn float_to_decimal_display<T>(fmt: &mut Formatter<'_>, num: &T) -> Result where T: flt2dec::DecodableFloat, { @@ -68,6 +87,7 @@ where if let Some(precision) = fmt.precision { float_to_decimal_common_exact(fmt, num, sign, precision) } else { + let min_precision = 0; float_to_decimal_common_shortest(fmt, num, sign, min_precision) } } @@ -145,19 +165,44 @@ where } } +fn float_to_general_debug<T>(fmt: &mut Formatter<'_>, num: &T) -> Result +where + T: flt2dec::DecodableFloat + GeneralFormat, +{ + let force_sign = fmt.sign_plus(); + let sign = match force_sign { + false => flt2dec::Sign::Minus, + true => flt2dec::Sign::MinusPlus, + }; + + if let Some(precision) = fmt.precision { + // this behavior of {:.PREC?} predates exponential formatting for {:?} + float_to_decimal_common_exact(fmt, num, sign, precision) + } else { + // since there is no precision, there will be no rounding + if num.already_rounded_value_should_use_exponential() { + let upper = false; + float_to_exponential_common_shortest(fmt, num, sign, upper) + } else { + let min_precision = 1; + float_to_decimal_common_shortest(fmt, num, sign, min_precision) + } + } +} + 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_decimal_common(fmt, self, 1) + float_to_general_debug(fmt, self) } } #[stable(feature = "rust1", since = "1.0.0")] impl Display for $ty { fn fmt(&self, fmt: &mut Formatter<'_>) -> Result { - float_to_decimal_common(fmt, self, 0) + float_to_decimal_display(fmt, self) } } diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index ad8106df198..83a922ae348 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -449,7 +449,7 @@ impl f32 { // private use internally. #[inline] #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] - const fn abs_private(self) -> f32 { + pub(crate) const fn abs_private(self) -> f32 { f32::from_bits(self.to_bits() & 0x7fff_ffff) } diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 6a48101e04f..4267260eea3 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -448,7 +448,7 @@ impl f64 { // private use internally. #[inline] #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] - const fn abs_private(self) -> f64 { + pub(crate) const fn abs_private(self) -> f64 { f64::from_bits(self.to_bits() & 0x7fff_ffff_ffff_ffff) } diff --git a/library/core/tests/fmt/float.rs b/library/core/tests/fmt/float.rs index bd0daf7a8eb..47a7400f76e 100644 --- a/library/core/tests/fmt/float.rs +++ b/library/core/tests/fmt/float.rs @@ -12,6 +12,16 @@ fn test_format_f64() { assert_eq!("1.23456789E3", format!("{:E}", 1234.56789f64)); assert_eq!("0.0", format!("{:?}", 0.0f64)); assert_eq!("1.01", format!("{:?}", 1.01f64)); + + let high_cutoff = 1e16_f64; + assert_eq!("1e16", format!("{:?}", high_cutoff)); + assert_eq!("-1e16", format!("{:?}", -high_cutoff)); + assert!(!is_exponential(&format!("{:?}", high_cutoff * (1.0 - 2.0 * f64::EPSILON)))); + assert_eq!("-3.0", format!("{:?}", -3f64)); + assert_eq!("0.0001", format!("{:?}", 0.0001f64)); + assert_eq!("9e-5", format!("{:?}", 0.00009f64)); + assert_eq!("1234567.9", format!("{:.1?}", 1234567.89f64)); + assert_eq!("1234.6", format!("{:.1?}", 1234.56789f64)); } #[test] @@ -28,4 +38,18 @@ fn test_format_f32() { assert_eq!("1.2345679E3", format!("{:E}", 1234.56789f32)); assert_eq!("0.0", format!("{:?}", 0.0f32)); assert_eq!("1.01", format!("{:?}", 1.01f32)); + + let high_cutoff = 1e16_f32; + assert_eq!("1e16", format!("{:?}", high_cutoff)); + assert_eq!("-1e16", format!("{:?}", -high_cutoff)); + assert!(!is_exponential(&format!("{:?}", high_cutoff * (1.0 - 2.0 * f32::EPSILON)))); + assert_eq!("-3.0", format!("{:?}", -3f32)); + assert_eq!("0.0001", format!("{:?}", 0.0001f32)); + assert_eq!("9e-5", format!("{:?}", 0.00009f32)); + assert_eq!("1234567.9", format!("{:.1?}", 1234567.89f32)); + assert_eq!("1234.6", format!("{:.1?}", 1234.56789f32)); +} + +fn is_exponential(s: &str) -> bool { + s.contains("e") || s.contains("E") } |
