Repository navigation
Closed
Labels
P3Real gap or design debt; scheduled workReal gap or design debt; scheduled workbugSomething isn't workingSomething isn't working
Description
Problem
ToString and TryFormat always write fixed-point notation, including every zero the exponent implies. Parse accepts any exponent that fits in an int, so a value can parse successfully and then fail to format, or format as an enormous string. Observed on 2.0.3:
| Value | ToString() |
TryFormat into 64 chars |
|---|---|---|
1E-2147483648 |
throws OverflowException |
throws ArgumentOutOfRangeException |
1E2147483647 |
throws ArgumentOutOfRangeException |
throws ArgumentOutOfRangeException |
1E-1000000 |
1,000,002 characters | returns false |
1E1000000 |
1,000,001 characters | returns false |
TryFormat should return false when the destination is too small rather than throw, as it does for the last two rows.
Cause
The formatting paths do their length arithmetic in int and negate or take the absolute value of the exponent:
ToStringsizes its buffer asSignificantDigits + int.Abs(Exponent) + ...(PreciseNumber.cslines 334 to 338).int.Absthrows forint.MinValue, and the sum overflows for exponents nearint.MaxValue.CountDecimalDigitsreturnsint.Abs(Exponent)(PreciseNumber.csline 657).TryFormatcomputesfractionalDigits = -Exponent(PreciseNumber.csline 1133), which wraps forint.MinValue, and derives slice lengths from the exponent.
#72 fixed this pattern in the conversion paths with range checks and by widening to long, but left formatting alone.
Suggested fix
- Do the length arithmetic in
long.TryFormatthen returnsfalsefor any length that doesn't fit, andToStringfails with a clear exception when the text can't exist as a string. - Consider scientific notation for extreme exponents. Supporting the
"E"and"G"standard format strings, or switching the default past some exponent the waydoubledoes, would make these values printable at all. Check how theformatargument is handled today before choosing. - Add tests for
int.MinValueandint.MaxValueexponents acrossToString,TryFormat, andCountDecimalDigits.
Repro
#:package ktsu.PreciseNumber@2.0.3
using System.Globalization;
using ktsu.PreciseNumber;
PreciseNumber tiny = PreciseNumber.Parse("1E-2147483648", NumberStyles.Float, CultureInfo.InvariantCulture);
Console.WriteLine(tiny.ToString(CultureInfo.InvariantCulture)); // OverflowExceptionMetadata
Metadata
Assignees
Labels
P3Real gap or design debt; scheduled workReal gap or design debt; scheduled workbugSomething isn't workingSomething isn't working
Activity