Skip to content

Parse values and zeros at the edges of the exponent range [patch] - #180

Merged
matt-edmondson merged 2 commits into
mainfrom
fix/178-parse-extreme-exponents
Oct 30, 2026
Merged

matt-edmondson merged 2 commits into
mainfrom
fix/178-parse-extreme-exponents

Conversation

@matt-edmondson

Copy link
Copy Markdown
Contributor

Fixes #178

What was wrong

Parse read the exponent text as an int and then ran checked(exponent - decimalDigits). That subtraction counted every written fraction digit, padding zeros included, and it happened before the value was normalized. So:

  • "2.500E-2147483647" and "1.0e-2147483648" threw OverflowException, even though 25e-2147483648 and 1e-2147483648 are representable.
  • ToString("E") output for values near int.MinValue could not be parsed back. At those magnitudes "E" is the only text form that fits in a string, so this broke round-tripping.
  • "0e99999999999" threw. decimal and double both return 0 for it.

The fix

The tail of Parse moves into a new FromParsedParts helper. The move keeps Parse under the CA1502 complexity limit. The helper:

  • drops trailing zeros from the collected digits before applying the exponent
  • reads the exponent as a long, applies the fraction and trailing-zero adjustments in checked long arithmetic, and throws OverflowException only if the final exponent falls outside int
  • returns Zero when every digit is zero, whatever the exponent says, including one too large for long. A malformed exponent ("0e", "0ex", "0e 5") still throws FormatException.

TryParse still reports a genuine overflow as false.

Tests

The new tests are in a new file, PreciseNumberParseExponentTests.cs:

  • Parse_AppliesTheExponentAfterDroppingPaddingZeros covers each input from the issue, plus negative and near-int.MaxValue cases, through both Parse and TryParse.
  • Parse_ReadsZeroWhateverItsWrittenExponent checks, for each input, that decimal returns 0 and that PreciseNumber returns Zero.
  • Parse_StillOverflowsForAValueOutsideTheExponentRange checks that genuine overflow still throws, or returns false from TryParse.
  • Parse_RejectsAMalformedExponentEvenOnZero
  • Parse_RoundTripsScientificTextAtTheEdgesOfTheExponentRange covers Parse(x.ToString("E")) for exponents within 10 of int.MinValue and int.MaxValue, which is the issue's acceptance criterion.

Results:

  • Without the fix, 9 of the new cases fail. With it, all pass.
  • The full suite passes: 536/536. The solution builds for every target framework with no warnings or analyzer findings.
  • I did not run the benchmarks. On the normal path the only addition is a backwards scan over trailing '0' characters that were already collected.

Open PR #176 also edits Parse, for NBSP separators, and PR #179 (#177) adds a style check at its top. Neither touches the exponent tail this PR changes, but whichever lands second may need a rebase.


Generated by Claude Code

Parse subtracted every written fraction digit from the exponent in checked
int arithmetic before normalizing, and read the exponent text as an int even
when the significand was zero. So "2.500E-2147483647" and "1.0e-2147483648"
threw OverflowException although the values fit, ToString("E") output near
int.MinValue could not be parsed back, and "0e99999999999" threw where decimal
and double return 0.

Parse now drops trailing zeros before applying the exponent, does that
arithmetic in long and range-checks only the result, and returns Zero for a
zero significand whatever its well-formed exponent.

Fixes #178

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017vfJW5ShapPU8BXcXvikJm
Comment thread PreciseNumber/PreciseNumber.cs Fixed
@sonarqubecloud

Copy link
Copy Markdown

@matt-edmondson
matt-edmondson merged commit 9a17419 into main Oct 30, 2026
15 checks passed
@matt-edmondson
matt-edmondson deleted the fix/178-parse-extreme-exponents branch October 30, 2026 02:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants