Skip to content

Commit e147063

Browse files
authored
Merge pull request #7413 from drinkcat/printf-nan
Fix `nan` print, simplify negative number printing.
2 parents 5f6a7d0 + 0c0d119 commit e147063

File tree

2 files changed

+64
-36
lines changed

2 files changed

+64
-36
lines changed

src/uucore/src/lib/features/format/num_format.rs

Lines changed: 32 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ impl Formatter for SignedInt {
8585
x.abs().to_string()
8686
};
8787

88-
let sign_indicator = get_sign_indicator(self.positive_sign, &x);
88+
let sign_indicator = get_sign_indicator(self.positive_sign, x.is_negative());
8989

9090
write_output(writer, sign_indicator, s, self.width, self.alignment)
9191
}
@@ -239,8 +239,9 @@ impl Default for Float {
239239
impl Formatter for Float {
240240
type Input = f64;
241241

242-
fn fmt(&self, writer: impl Write, x: Self::Input) -> std::io::Result<()> {
243-
let mut s = if x.is_finite() {
242+
fn fmt(&self, writer: impl Write, f: Self::Input) -> std::io::Result<()> {
243+
let x = f.abs();
244+
let s = if x.is_finite() {
244245
match self.variant {
245246
FloatVariant::Decimal => {
246247
format_float_decimal(x, self.precision, self.force_decimal)
@@ -259,11 +260,7 @@ impl Formatter for Float {
259260
format_float_non_finite(x, self.case)
260261
};
261262

262-
// The format function will parse `x` together with its sign char,
263-
// which should be placed in `sign_indicator`. So drop it here
264-
s = if x < 0. { s[1..].to_string() } else { s };
265-
266-
let sign_indicator = get_sign_indicator(self.positive_sign, &x);
263+
let sign_indicator = get_sign_indicator(self.positive_sign, f.is_sign_negative());
267264

268265
write_output(writer, sign_indicator, s, self.width, self.alignment)
269266
}
@@ -309,8 +306,8 @@ impl Formatter for Float {
309306
}
310307
}
311308

312-
fn get_sign_indicator<T: PartialOrd + Default>(sign: PositiveSign, x: &T) -> String {
313-
if *x >= T::default() {
309+
fn get_sign_indicator(sign: PositiveSign, negative: bool) -> String {
310+
if !negative {
314311
match sign {
315312
PositiveSign::None => String::new(),
316313
PositiveSign::Plus => String::from("+"),
@@ -324,13 +321,15 @@ fn get_sign_indicator<T: PartialOrd + Default>(sign: PositiveSign, x: &T) -> Str
324321
fn format_float_non_finite(f: f64, case: Case) -> String {
325322
debug_assert!(!f.is_finite());
326323
let mut s = format!("{f}");
327-
if case == Case::Uppercase {
328-
s.make_ascii_uppercase();
324+
match case {
325+
Case::Lowercase => s.make_ascii_lowercase(), // Forces NaN back to nan.
326+
Case::Uppercase => s.make_ascii_uppercase(),
329327
}
330328
s
331329
}
332330

333331
fn format_float_decimal(f: f64, precision: usize, force_decimal: ForceDecimal) -> String {
332+
debug_assert!(!f.is_sign_negative());
334333
if precision == 0 && force_decimal == ForceDecimal::Yes {
335334
format!("{f:.0}.")
336335
} else {
@@ -344,6 +343,7 @@ fn format_float_scientific(
344343
case: Case,
345344
force_decimal: ForceDecimal,
346345
) -> String {
346+
debug_assert!(!f.is_sign_negative());
347347
let exp_char = match case {
348348
Case::Lowercase => 'e',
349349
Case::Uppercase => 'E',
@@ -383,6 +383,7 @@ fn format_float_shortest(
383383
case: Case,
384384
force_decimal: ForceDecimal,
385385
) -> String {
386+
debug_assert!(!f.is_sign_negative());
386387
// Precision here is about how many digits should be displayed
387388
// instead of how many digits for the fractional part, this means that if
388389
// we pass this to rust's format string, it's always gonna be one less.
@@ -459,21 +460,21 @@ fn format_float_hexadecimal(
459460
case: Case,
460461
force_decimal: ForceDecimal,
461462
) -> String {
462-
let (sign, first_digit, mantissa, exponent) = if f == 0.0 {
463-
("", 0, 0, 0)
463+
debug_assert!(!f.is_sign_negative());
464+
let (first_digit, mantissa, exponent) = if f == 0.0 {
465+
(0, 0, 0)
464466
} else {
465467
let bits = f.to_bits();
466-
let sign = if (bits >> 63) == 1 { "-" } else { "" };
467468
let exponent_bits = ((bits >> 52) & 0x7ff) as i64;
468469
let exponent = exponent_bits - 1023;
469470
let mantissa = bits & 0xf_ffff_ffff_ffff;
470-
(sign, 1, mantissa, exponent)
471+
(1, mantissa, exponent)
471472
};
472473

473474
let mut s = match (precision, force_decimal) {
474-
(0, ForceDecimal::No) => format!("{sign}0x{first_digit}p{exponent:+}"),
475-
(0, ForceDecimal::Yes) => format!("{sign}0x{first_digit}.p{exponent:+}"),
476-
_ => format!("{sign}0x{first_digit}.{mantissa:0>13x}p{exponent:+}"),
475+
(0, ForceDecimal::No) => format!("0x{first_digit}p{exponent:+}"),
476+
(0, ForceDecimal::Yes) => format!("0x{first_digit}.p{exponent:+}"),
477+
_ => format!("0x{first_digit}.{mantissa:0>13x}p{exponent:+}"),
477478
};
478479

479480
if case == Case::Uppercase {
@@ -550,6 +551,18 @@ mod test {
550551
assert_eq!(f(8), "010");
551552
}
552553

554+
#[test]
555+
fn non_finite_float() {
556+
use super::format_float_non_finite;
557+
let f = |x| format_float_non_finite(x, Case::Lowercase);
558+
assert_eq!(f(f64::NAN), "nan");
559+
assert_eq!(f(f64::INFINITY), "inf");
560+
561+
let f = |x| format_float_non_finite(x, Case::Uppercase);
562+
assert_eq!(f(f64::NAN), "NAN");
563+
assert_eq!(f(f64::INFINITY), "INF");
564+
}
565+
553566
#[test]
554567
fn decimal_float() {
555568
use super::format_float_decimal;
@@ -662,22 +675,14 @@ mod test {
662675
assert_eq!(f(0.125), "0x1.0000000000000p-3");
663676
assert_eq!(f(256.0), "0x1.0000000000000p+8");
664677
assert_eq!(f(65536.0), "0x1.0000000000000p+16");
665-
assert_eq!(f(-0.00001), "-0x1.4f8b588e368f1p-17");
666-
assert_eq!(f(-0.125), "-0x1.0000000000000p-3");
667-
assert_eq!(f(-256.0), "-0x1.0000000000000p+8");
668-
assert_eq!(f(-65536.0), "-0x1.0000000000000p+16");
669678

670679
let f = |x| format_float_hexadecimal(x, 0, Case::Lowercase, ForceDecimal::No);
671680
assert_eq!(f(0.125), "0x1p-3");
672681
assert_eq!(f(256.0), "0x1p+8");
673-
assert_eq!(f(-0.125), "-0x1p-3");
674-
assert_eq!(f(-256.0), "-0x1p+8");
675682

676683
let f = |x| format_float_hexadecimal(x, 0, Case::Lowercase, ForceDecimal::Yes);
677684
assert_eq!(f(0.125), "0x1.p-3");
678685
assert_eq!(f(256.0), "0x1.p+8");
679-
assert_eq!(f(-0.125), "-0x1.p-3");
680-
assert_eq!(f(-256.0), "-0x1.p+8");
681686
}
682687

683688
#[test]
@@ -703,11 +708,6 @@ mod test {
703708
assert_eq!(f(0.001171875), "0.00117187");
704709
assert_eq!(f(0.0001171875), "0.000117187");
705710
assert_eq!(f(0.001171875001), "0.00117188");
706-
assert_eq!(f(-0.1171875), "-0.117188");
707-
assert_eq!(f(-0.01171875), "-0.0117188");
708-
assert_eq!(f(-0.001171875), "-0.00117187");
709-
assert_eq!(f(-0.0001171875), "-0.000117187");
710-
assert_eq!(f(-0.001171875001), "-0.00117188");
711711
}
712712

713713
#[test]
@@ -718,9 +718,5 @@ mod test {
718718
assert_eq!(f(0.0001), "0.0001");
719719
assert_eq!(f(0.00001), "1e-05");
720720
assert_eq!(f(0.000001), "1e-06");
721-
assert_eq!(f(-0.001), "-0.001");
722-
assert_eq!(f(-0.0001), "-0.0001");
723-
assert_eq!(f(-0.00001), "-1e-05");
724-
assert_eq!(f(-0.000001), "-1e-06");
725721
}
726722
}

tests/by-util/test_printf.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,14 @@ fn sub_num_dec_trunc() {
380380
.stdout_only("pi is ~ 3.14159");
381381
}
382382

383+
#[test]
384+
fn sub_num_sci_negative() {
385+
new_ucmd!()
386+
.args(&["-1234 is %e", "-1234"])
387+
.succeeds()
388+
.stdout_only("-1234 is -1.234000e+03");
389+
}
390+
383391
#[cfg_attr(not(feature = "test_unimplemented"), ignore)]
384392
#[test]
385393
fn sub_num_hex_float_lower() {
@@ -886,6 +894,30 @@ fn float_with_zero_precision_should_pad() {
886894
.stdout_only("-01");
887895
}
888896

897+
#[test]
898+
fn float_non_finite() {
899+
new_ucmd!()
900+
.args(&[
901+
"%f %f %F %f %f %F",
902+
"nan",
903+
"-nan",
904+
"nan",
905+
"inf",
906+
"-inf",
907+
"inf",
908+
])
909+
.succeeds()
910+
.stdout_only("nan -nan NAN inf -inf INF");
911+
}
912+
913+
#[test]
914+
fn float_zero_neg_zero() {
915+
new_ucmd!()
916+
.args(&["%f %f", "0.0", "-0.0"])
917+
.succeeds()
918+
.stdout_only("0.000000 -0.000000");
919+
}
920+
889921
#[test]
890922
fn precision_check() {
891923
new_ucmd!()

0 commit comments

Comments
 (0)