Skip to content

NumberUtils: accept leading "+" for integers, reject overflowing exponents - #104

Merged
nicost merged 1 commit into
nicost:localefrom
hinderling:numberutils-followups
Aug 21, 2026
Merged

NumberUtils: accept leading "+" for integers, reject overflowing exponents#104
nicost merged 1 commit into
nicost:localefrom
hinderling:numberutils-followups

Conversation

@hinderling

Copy link
Copy Markdown

The three small leftovers from my last round of testing on the split-locale machine in micro-manager#2437. All three verified on that machine (Windows UI locale en-US, regional format en-CH).

1. Integer fields rejected a leading +. displayStringToDouble("+3") returns 3.0, but displayStringToInt("+3") failed, so the same string parsed in one field and not the other. Widening the pattern alone would not have worked: DecimalFormat.parse() cannot handle a leading + in any locale (I checked en_CH, en_US and de_DE -- all return null), so it is stripped before parsing instead.

2. An overflowing exponent silently became Infinity. The pattern allows an exponent and Double.parseDouble overflows quietly, so 1e400 returned Infinity even though the literal "Infinity" is explicitly rejected a few lines earlier. Now any non-finite result is rejected. I deliberately left 1e-400 -> 0.0 alone: underflowing to zero is a reasonable answer for a physical quantity, overflowing to infinity is not.

3. NumberUtilsTest's @Before guard checked the wrong locale category. It skips based on Locale.getDefault(), but the code it guards formats via NumberFormat.getInstance(), i.e. Locale.getDefault(Locale.Category.FORMAT). On a split-locale system those disagree, so the guard passed and doubleToStringIsCorrect then failed with expected:<0[.]0001> but was:<0[,]0001>. Guarding on the same category the code uses fixes it.

Worth noting for later: a test cannot pin the locale here, since FORMAT is built in a static initializer and Locale.setDefault() after class load has no effect on it. Skipping is the only option unless the assertions derive the separator themselves.

Verification

Both suites, on this machine and under simulated de-DE and en-US:

NumberUtilsTest          OK (5 tests)
NumberUtilsLocaleTest    OK (16 tests)

NumberUtilsTest passes on this machine for the first time. I also re-ran a brute force over ~1.1M short strings under en-CH(split), de-DE, en-US and fr-FR: no input is silently parsed as a different number, and no plain decimal number is rejected, in any of them. Round trip, negatives, Integer.MIN_VALUE/MAX_VALUE and Long.MAX_VALUE all still fine.

…ing exponents

Three follow-ups to the locale parsing work:

- parseIntegerStrict() rejected a leading "+", while displayStringToDouble()
  accepts one, so "+3" parsed as a double but not as an int. DecimalFormat
  cannot parse "+" in any locale, so strip it rather than widening the pattern.

- displayStringToDouble() allows an exponent, and Double.parseDouble() silently
  overflows it to Infinity, so "1e400" returned Infinity even though the literal
  "Infinity" is explicitly rejected. Reject any non-finite result.

- NumberUtilsTest's @before guard skipped based on Locale.getDefault(), but the
  code it guards formats via Locale.getDefault(Locale.Category.FORMAT). On a
  split-locale system (UI locale en-US, regional format en-CH) those disagree, so
  the guard passed and doubleToStringIsCorrect then failed. Guard on the same
  category the code under test uses.
@nicost
nicost merged commit 65b1b2c into nicost:locale Aug 21, 2026
5 checks passed
@hinderling
hinderling deleted the numberutils-followups branch August 26, 2026 08:58
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.

2 participants