From b7dcaa34dae729bda3055910e2d644da0104c949 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Fri, 7 Mar 2025 12:18:03 +0100 Subject: [PATCH] uucore: format: print absolute value of float, then add sign Simplifies the code, but also fixes printing of negative and positive `NaN`: `cargo run printf "%f %f\n" nan -nan` Fixes part 2 of #7412. --- .../src/lib/features/format/num_format.rs | 51 +++++++------------ 1 file changed, 17 insertions(+), 34 deletions(-) diff --git a/src/uucore/src/lib/features/format/num_format.rs b/src/uucore/src/lib/features/format/num_format.rs index b0dd1b945..0a4e47528 100644 --- a/src/uucore/src/lib/features/format/num_format.rs +++ b/src/uucore/src/lib/features/format/num_format.rs @@ -85,7 +85,7 @@ impl Formatter for SignedInt { x.abs().to_string() }; - let sign_indicator = get_sign_indicator(self.positive_sign, &x); + let sign_indicator = get_sign_indicator(self.positive_sign, x.is_negative()); write_output(writer, sign_indicator, s, self.width, self.alignment) } @@ -239,8 +239,9 @@ impl Default for Float { impl Formatter for Float { type Input = f64; - fn fmt(&self, writer: impl Write, x: Self::Input) -> std::io::Result<()> { - let mut s = if x.is_finite() { + fn fmt(&self, writer: impl Write, f: Self::Input) -> std::io::Result<()> { + let x = f.abs(); + let s = if x.is_finite() { match self.variant { FloatVariant::Decimal => { format_float_decimal(x, self.precision, self.force_decimal) @@ -259,11 +260,7 @@ impl Formatter for Float { format_float_non_finite(x, self.case) }; - // The format function will parse `x` together with its sign char, - // which should be placed in `sign_indicator`. So drop it here - s = if x < 0. { s[1..].to_string() } else { s }; - - let sign_indicator = get_sign_indicator(self.positive_sign, &x); + let sign_indicator = get_sign_indicator(self.positive_sign, f.is_sign_negative()); write_output(writer, sign_indicator, s, self.width, self.alignment) } @@ -309,8 +306,8 @@ impl Formatter for Float { } } -fn get_sign_indicator(sign: PositiveSign, x: &T) -> String { - if *x >= T::default() { +fn get_sign_indicator(sign: PositiveSign, negative: bool) -> String { + if !negative { match sign { PositiveSign::None => String::new(), PositiveSign::Plus => String::from("+"), @@ -332,6 +329,7 @@ fn format_float_non_finite(f: f64, case: Case) -> String { } fn format_float_decimal(f: f64, precision: usize, force_decimal: ForceDecimal) -> String { + debug_assert!(!f.is_sign_negative()); if precision == 0 && force_decimal == ForceDecimal::Yes { format!("{f:.0}.") } else { @@ -345,6 +343,7 @@ fn format_float_scientific( case: Case, force_decimal: ForceDecimal, ) -> String { + debug_assert!(!f.is_sign_negative()); let exp_char = match case { Case::Lowercase => 'e', Case::Uppercase => 'E', @@ -384,6 +383,7 @@ fn format_float_shortest( case: Case, force_decimal: ForceDecimal, ) -> String { + debug_assert!(!f.is_sign_negative()); // Precision here is about how many digits should be displayed // instead of how many digits for the fractional part, this means that if // we pass this to rust's format string, it's always gonna be one less. @@ -460,21 +460,21 @@ fn format_float_hexadecimal( case: Case, force_decimal: ForceDecimal, ) -> String { - let (sign, first_digit, mantissa, exponent) = if f == 0.0 { - ("", 0, 0, 0) + debug_assert!(!f.is_sign_negative()); + let (first_digit, mantissa, exponent) = if f == 0.0 { + (0, 0, 0) } else { let bits = f.to_bits(); - let sign = if (bits >> 63) == 1 { "-" } else { "" }; let exponent_bits = ((bits >> 52) & 0x7ff) as i64; let exponent = exponent_bits - 1023; let mantissa = bits & 0xf_ffff_ffff_ffff; - (sign, 1, mantissa, exponent) + (1, mantissa, exponent) }; let mut s = match (precision, force_decimal) { - (0, ForceDecimal::No) => format!("{sign}0x{first_digit}p{exponent:+}"), - (0, ForceDecimal::Yes) => format!("{sign}0x{first_digit}.p{exponent:+}"), - _ => format!("{sign}0x{first_digit}.{mantissa:0>13x}p{exponent:+}"), + (0, ForceDecimal::No) => format!("0x{first_digit}p{exponent:+}"), + (0, ForceDecimal::Yes) => format!("0x{first_digit}.p{exponent:+}"), + _ => format!("0x{first_digit}.{mantissa:0>13x}p{exponent:+}"), }; if case == Case::Uppercase { @@ -675,22 +675,14 @@ mod test { assert_eq!(f(0.125), "0x1.0000000000000p-3"); assert_eq!(f(256.0), "0x1.0000000000000p+8"); assert_eq!(f(65536.0), "0x1.0000000000000p+16"); - assert_eq!(f(-0.00001), "-0x1.4f8b588e368f1p-17"); - assert_eq!(f(-0.125), "-0x1.0000000000000p-3"); - assert_eq!(f(-256.0), "-0x1.0000000000000p+8"); - assert_eq!(f(-65536.0), "-0x1.0000000000000p+16"); let f = |x| format_float_hexadecimal(x, 0, Case::Lowercase, ForceDecimal::No); assert_eq!(f(0.125), "0x1p-3"); assert_eq!(f(256.0), "0x1p+8"); - assert_eq!(f(-0.125), "-0x1p-3"); - assert_eq!(f(-256.0), "-0x1p+8"); let f = |x| format_float_hexadecimal(x, 0, Case::Lowercase, ForceDecimal::Yes); assert_eq!(f(0.125), "0x1.p-3"); assert_eq!(f(256.0), "0x1.p+8"); - assert_eq!(f(-0.125), "-0x1.p-3"); - assert_eq!(f(-256.0), "-0x1.p+8"); } #[test] @@ -716,11 +708,6 @@ mod test { assert_eq!(f(0.001171875), "0.00117187"); assert_eq!(f(0.0001171875), "0.000117187"); assert_eq!(f(0.001171875001), "0.00117188"); - assert_eq!(f(-0.1171875), "-0.117188"); - assert_eq!(f(-0.01171875), "-0.0117188"); - assert_eq!(f(-0.001171875), "-0.00117187"); - assert_eq!(f(-0.0001171875), "-0.000117187"); - assert_eq!(f(-0.001171875001), "-0.00117188"); } #[test] @@ -731,9 +718,5 @@ mod test { assert_eq!(f(0.0001), "0.0001"); assert_eq!(f(0.00001), "1e-05"); assert_eq!(f(0.000001), "1e-06"); - assert_eq!(f(-0.001), "-0.001"); - assert_eq!(f(-0.0001), "-0.0001"); - assert_eq!(f(-0.00001), "-1e-05"); - assert_eq!(f(-0.000001), "-1e-06"); } }