Skip to content

numfmt: reject numbers with too many digits like GNU - #15002

Open
sap1110 wants to merge 1 commit into
uutils:mainfrom
sap1110:numfmt-too-many-digits
Open

sap1110 wants to merge 1 commit into
uutils:mainfrom
sap1110:numfmt-too-many-digits

Conversation

@sap1110

@sap1110 sap1110 commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

numfmt would take a number with a huge number of digits, round it without saying anything, and print something that didn't match what you typed.

Now it rejects any number with more than 33 digits on either side of the decimal point. It prints value too large to be converted: '<input>' and exits with 2, same as GNU.

I matched GNU's behaviour by running it, not by reading its code:

  • Leading zeros don't count.
  • Trailing zeros in the fraction do count, but a fraction that's all zeros is fine.
  • The whole part and the fraction each have their own limit of 33.
  • The check happens before suffix parsing, so K or Ki after the number doesn't change the error.
  • --invalid=warn/fail/ignore treat it like any other bad input.

One small extra: when the terminal error display is on, the long number gets underlined, and the suffix hint (which was misleading here) is no longer shown.

There are 8 new tests: whole part, fraction, leading zeros, negatives, numbers before a suffix, the --invalid modes, and the input from the issue. Each error test checks stderr, stdout and the exit code.

Closes #12855

Comment thread src/uu/numfmt/src/format.rs Outdated
}

/// GNU rejects an integer or fraction part with more than this many digits.
const MAX_ACCEPTABLE_DIGITS: usize = 33;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is the GNU identifier name, and the PR description quotes GNU internals too
please don't look at the GNU source, just the behavior. could you rename it, e.g. MAX_DIGITS?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right. I looked at GNU's source before I wrote the first version, and I shouldn't have done that.

I've thrown that version out and rewritten it from scratch. This time I only ran GNU numfmt and checked how it behaves, without opening its code. The new version is force-pushed.

I've also fixed your other two points:

  • The constant is now MAX_DIGITS.
  • The integer and fraction parts are now two separate variables, whole and fraction, so neither name is wrong anymore.
  • Every error test now checks stdout as well.

Let me know if anything still looks off.

Comment thread src/uu/numfmt/src/format.rs Outdated
let dec_sep = locale_decimal_separator();

// Like GNU, limit the integer and fraction digit runs separately.
let int_part = s.strip_prefix('-').unwrap_or(s);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

int_part also holds the fraction here, maybe unsigned?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rewrite doesn't have int_part anymore. It counts the integer part and the fraction part in two separate variables, whole and fraction, so I didn't need a single name like unsigned.

Comment thread tests/by-util/test_numfmt.rs Outdated
.stdout_only("112Q\n");
}
for input in [format!("0.{max_digits}"), format!("0.{zeros}{max_digits}")] {
new_ucmd!().args(&["--to=si", &input]).succeeds();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please check stdout here too, otherwise we don't know what we print

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Every error test checks stdout now too. It's either empty, or, in the --invalid=warn/fail tests, exactly what gets printed before the error.

A number whose whole part or fraction has more than 33 digits, not
counting leading zeros, is now refused with "value too large to be
converted" instead of being silently rounded, matching GNU numfmt.

Closes uutils#12855
@sap1110
sap1110 force-pushed the numfmt-too-many-digits branch from 3c7b038 to e93e0b3 Compare October 1, 2026 13:26
@github-actions

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown

GNU testsuite comparison:

Congrats! The gnu test tests/id/setgid is no longer failing!

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug(numfmt): some numbers cause incorrect conversion and logic issues

2 participants