1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-27 11:07:44 +00:00

Merge pull request #7413 from drinkcat/printf-nan

Fix `nan` print, simplify negative number printing.
This commit is contained in:
Dorian Péron 2025-03-14 14:23:31 +01:00 committed by GitHub
commit e147063e26
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 64 additions and 36 deletions

View file

@ -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<T: PartialOrd + Default>(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("+"),
@ -324,13 +321,15 @@ fn get_sign_indicator<T: PartialOrd + Default>(sign: PositiveSign, x: &T) -> Str
fn format_float_non_finite(f: f64, case: Case) -> String {
debug_assert!(!f.is_finite());
let mut s = format!("{f}");
if case == Case::Uppercase {
s.make_ascii_uppercase();
match case {
Case::Lowercase => s.make_ascii_lowercase(), // Forces NaN back to nan.
Case::Uppercase => s.make_ascii_uppercase(),
}
s
}
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 {
@ -344,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',
@ -383,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.
@ -459,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 {
@ -550,6 +551,18 @@ mod test {
assert_eq!(f(8), "010");
}
#[test]
fn non_finite_float() {
use super::format_float_non_finite;
let f = |x| format_float_non_finite(x, Case::Lowercase);
assert_eq!(f(f64::NAN), "nan");
assert_eq!(f(f64::INFINITY), "inf");
let f = |x| format_float_non_finite(x, Case::Uppercase);
assert_eq!(f(f64::NAN), "NAN");
assert_eq!(f(f64::INFINITY), "INF");
}
#[test]
fn decimal_float() {
use super::format_float_decimal;
@ -662,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]
@ -703,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]
@ -718,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");
}
}

View file

@ -380,6 +380,14 @@ fn sub_num_dec_trunc() {
.stdout_only("pi is ~ 3.14159");
}
#[test]
fn sub_num_sci_negative() {
new_ucmd!()
.args(&["-1234 is %e", "-1234"])
.succeeds()
.stdout_only("-1234 is -1.234000e+03");
}
#[cfg_attr(not(feature = "test_unimplemented"), ignore)]
#[test]
fn sub_num_hex_float_lower() {
@ -886,6 +894,30 @@ fn float_with_zero_precision_should_pad() {
.stdout_only("-01");
}
#[test]
fn float_non_finite() {
new_ucmd!()
.args(&[
"%f %f %F %f %f %F",
"nan",
"-nan",
"nan",
"inf",
"-inf",
"inf",
])
.succeeds()
.stdout_only("nan -nan NAN inf -inf INF");
}
#[test]
fn float_zero_neg_zero() {
new_ucmd!()
.args(&["%f %f", "0.0", "-0.0"])
.succeeds()
.stdout_only("0.000000 -0.000000");
}
#[test]
fn precision_check() {
new_ucmd!()