From 209592b96ed29c3e8f01c1201a2f6a8e81d960b2 Mon Sep 17 00:00:00 2001 From: Yunus Ozen <94426948+ozenyunus35@users.noreply.github.com> Date: Thu, 22 Oct 2026 20:02:23 +0300 Subject: [PATCH 1/2] numfmt: round values printed without a suffix With --to=si/iec, a value printed without a suffix ignored --round, and the suffix was picked from the unrounded value, so 999.9 with --round=down became 0.9k instead of 999. Fixes #15574 --- src/uu/numfmt/src/format.rs | 14 ++++++++++++-- tests/by-util/test_numfmt.rs | 23 +++++++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/uu/numfmt/src/format.rs b/src/uu/numfmt/src/format.rs index a6416595b1e..c371504fbb8 100644 --- a/src/uu/numfmt/src/format.rs +++ b/src/uu/numfmt/src/format.rs @@ -584,8 +584,18 @@ fn consider_suffix( Unit::None => return Ok((n, None)), }; + // The suffix is chosen from the value as it will be printed, so with + // `--round=down` 999.9 stays 999 instead of becoming 0.9k. + let unscaled = if is_precision_specified { + round_with_precision(n, round_method, 0) + } else { + div_round(n, 1.0, round_method) + }; + if unscaled.abs() <= bases[1] - 1.0 { + return Ok((n, None)); + } + let i = match abs_n { - _ if abs_n <= bases[1] - 1.0 => return Ok((n, None)), _ if abs_n < bases[2] => 1, _ if abs_n < bases[3] => 2, _ if abs_n < bases[4] => 3, @@ -745,7 +755,7 @@ fn transform_to( let i2 = round_with_precision(i2, round_method, 0); localize(format_float(i2, precision)) } - None => localize(format_float(i2, 0)), + None => localize(format_float(div_round(i2, 1.0, round_method), 0)), Some(s) if precision > 0 && precision <= u16::MAX.into() => localize(format!( "{i2:.precision$}{unit_separator}{}", DisplayableSuffix(s, opts.to), diff --git a/tests/by-util/test_numfmt.rs b/tests/by-util/test_numfmt.rs index 036da1c09cc..4b4550b1fd0 100644 --- a/tests/by-util/test_numfmt.rs +++ b/tests/by-util/test_numfmt.rs @@ -616,6 +616,29 @@ fn test_round_with_to_unit() { } } +#[test] +fn test_round_without_suffix() { + // A value printed without a suffix is rounded with the method too, and + // the suffix is only added if the rounded value needs one. + for (method, exp) in [ + ("from-zero", ["13", "-13", "1.0k", "1.0K"]), + ("towards-zero", ["12", "-12", "999", "1023"]), + ("up", ["13", "-12", "1.0k", "1.0K"]), + ("down", ["12", "-13", "999", "1023"]), + ("nearest", ["12", "-12", "1.0k", "1.0K"]), + ] { + let round = format!("--round={method}"); + new_ucmd!() + .args(&["--to=si", &round, "--", "12.1", "-12.1", "999.9"]) + .succeeds() + .stdout_only(exp[..3].join("\n") + "\n"); + new_ucmd!() + .args(&["--to=iec", &round, "1023.9"]) + .succeeds() + .stdout_only(format!("{}\n", exp[3])); + } +} + #[test] fn test_to_unit_with_unitless_small_value_uses_display_rounding() { new_ucmd!() From 678a2527437934e7948379b32d11496831fe3b51 Mon Sep 17 00:00:00 2001 From: Yunus Ozen <94426948+ozenyunus35@users.noreply.github.com> Date: Thu, 22 Oct 2026 21:07:14 +0300 Subject: [PATCH 2/2] numfmt: return the rounded no-suffix value from consider_suffix Also cover the --format precision branch in the test. --- src/uu/numfmt/src/format.rs | 4 ++-- tests/by-util/test_numfmt.rs | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/uu/numfmt/src/format.rs b/src/uu/numfmt/src/format.rs index c371504fbb8..270039728c5 100644 --- a/src/uu/numfmt/src/format.rs +++ b/src/uu/numfmt/src/format.rs @@ -592,7 +592,7 @@ fn consider_suffix( div_round(n, 1.0, round_method) }; if unscaled.abs() <= bases[1] - 1.0 { - return Ok((n, None)); + return Ok((unscaled, None)); } let i = match abs_n { @@ -755,7 +755,7 @@ fn transform_to( let i2 = round_with_precision(i2, round_method, 0); localize(format_float(i2, precision)) } - None => localize(format_float(div_round(i2, 1.0, round_method), 0)), + None => localize(format_float(i2, 0)), Some(s) if precision > 0 && precision <= u16::MAX.into() => localize(format!( "{i2:.precision$}{unit_separator}{}", DisplayableSuffix(s, opts.to), diff --git a/tests/by-util/test_numfmt.rs b/tests/by-util/test_numfmt.rs index 4b4550b1fd0..2ce83c583b5 100644 --- a/tests/by-util/test_numfmt.rs +++ b/tests/by-util/test_numfmt.rs @@ -637,6 +637,21 @@ fn test_round_without_suffix() { .succeeds() .stdout_only(format!("{}\n", exp[3])); } + + // With a `--format` precision, the value is rounded to an integer first. + for (method, exp) in [ + ("from-zero", "13.0\n-13.0\n1.0k\n2.0\n"), + ("towards-zero", "12.0\n-12.0\n999.0\n1.0\n"), + ("up", "13.0\n-12.0\n1.0k\n2.0\n"), + ("down", "12.0\n-13.0\n999.0\n1.0\n"), + ("nearest", "12.0\n-12.0\n1.0k\n1.0\n"), + ] { + new_ucmd!() + .args(&["--to=si", "--format=%.1f", &format!("--round={method}")]) + .args(&["--", "12.1", "-12.1", "999.9", "1.44"]) + .succeeds() + .stdout_only(exp); + } } #[test]