What's wrong
PreciseNumber.Parse(ReadOnlySpan<char>, NumberStyles, IFormatProvider?) (PreciseNumber/PreciseNumber.cs, around lines 1169-1310) applies only some of the NumberStyles flags. Whitespace trimming and AllowThousands follow the style. These parts do not:
- The decimal separator is accepted whether or not
AllowDecimalPoint is set.
- The exponent (
e/E) is accepted whether or not AllowExponent is set. Its digits then go through int.Parse(..., NumberStyles.Integer), which allows whitespace inside the exponent.
- A leading
- is always accepted (ReadLeadingSign, about line 1295). Only the positive sign is gated on AllowLeadingSign.
Reproduction (current main, net10.0, invariant culture)
| Call |
PreciseNumber |
decimal.TryParse with the same style |
Parse("1.5", NumberStyles.Integer, inv) |
1.5 |
false |
Parse("1e5", NumberStyles.Integer, inv) |
100000 |
false |
Parse("-5", NumberStyles.None, inv) |
-5 |
false |
Parse("1e 5", NumberStyles.Float, inv) |
100000 |
false |
TryParse("1.5", NumberStyles.Integer, inv, out r) |
true, r = 1.5 |
false |
Why it matters
PreciseNumber implements INumberBase<T>. Callers use T.TryParse(s, NumberStyles.Integer, ...) to check that input is a whole number, and NumberStyles.None to reject signs. With T = PreciseNumber those checks pass for fractional, exponent or negative text. The code then gets a value it had explicitly excluded, for example a count of -5 or 1.5, with no error. Every other BCL numeric type rejects these inputs.
Suggested fix / acceptance criteria
- Accept the decimal separator only when
style.HasFlag(NumberStyles.AllowDecimalPoint).
- Accept
e/E only when style.HasFlag(NumberStyles.AllowExponent). Parse the exponent digits with NumberStyles.AllowLeadingSign only, not Integer, so "1e 5" is rejected.
- Accept a leading negative sign only when
style.HasFlag(NumberStyles.AllowLeadingSign), matching the positive-sign check.
- Add tests that compare against
decimal.TryParse for the inputs in the table above. Include these cases, which must keep working: NumberStyles.Float / Any / the default Parse(string, provider) overload on "1.5", "-1.5e-3" and "1,234.5" (with Any).
What's wrong
PreciseNumber.Parse(ReadOnlySpan<char>, NumberStyles, IFormatProvider?)(PreciseNumber/PreciseNumber.cs, around lines 1169-1310) applies only some of theNumberStylesflags. Whitespace trimming andAllowThousandsfollow the style. These parts do not:AllowDecimalPointis set.e/E) is accepted whether or notAllowExponentis set. Its digits then go throughint.Parse(..., NumberStyles.Integer), which allows whitespace inside the exponent.-is always accepted (ReadLeadingSign, about line 1295). Only the positive sign is gated onAllowLeadingSign.Reproduction (current
main, net10.0, invariant culture)decimal.TryParsewith the same styleParse("1.5", NumberStyles.Integer, inv)1.5Parse("1e5", NumberStyles.Integer, inv)100000Parse("-5", NumberStyles.None, inv)-5Parse("1e 5", NumberStyles.Float, inv)100000TryParse("1.5", NumberStyles.Integer, inv, out r)true,r = 1.5Why it matters
PreciseNumberimplementsINumberBase<T>. Callers useT.TryParse(s, NumberStyles.Integer, ...)to check that input is a whole number, andNumberStyles.Noneto reject signs. WithT = PreciseNumberthose checks pass for fractional, exponent or negative text. The code then gets a value it had explicitly excluded, for example a count of-5or1.5, with no error. Every other BCL numeric type rejects these inputs.Suggested fix / acceptance criteria
style.HasFlag(NumberStyles.AllowDecimalPoint).e/Eonly whenstyle.HasFlag(NumberStyles.AllowExponent). Parse the exponent digits withNumberStyles.AllowLeadingSignonly, notInteger, so"1e 5"is rejected.style.HasFlag(NumberStyles.AllowLeadingSign), matching the positive-sign check.decimal.TryParsefor the inputs in the table above. Include these cases, which must keep working:NumberStyles.Float/Any/ the defaultParse(string, provider)overload on"1.5","-1.5e-3"and"1,234.5"(withAny).