diff --git a/PreciseNumber.Test/PreciseNumberParseTrailingNulTests.cs b/PreciseNumber.Test/PreciseNumberParseTrailingNulTests.cs index 7fce253..37d054d 100644 --- a/PreciseNumber.Test/PreciseNumberParseTrailingNulTests.cs +++ b/PreciseNumber.Test/PreciseNumberParseTrailingNulTests.cs @@ -19,6 +19,8 @@ public class PreciseNumberParseTrailingNulTests [DataRow("1e3 \0", NumberStyles.Float)] [DataRow("12345\0\0\0\0\0\0\0\0", NumberStyles.Integer)] [DataRow("-0.25\0", NumberStyles.Number)] + [DataRow("3e1\0", NumberStyles.Float)] + [DataRow("3e1\0\0", NumberStyles.Float)] public void ParseIgnoresTrailingNulsAsDecimalDoes(string text, NumberStyles style) { CultureInfo culture = CultureInfo.InvariantCulture; @@ -51,4 +53,23 @@ public void ParseStillRejectsNulsThatAreNotTheLastCharacters(string text, Number Assert.IsFalse(PreciseNumber.TryParse(text.AsSpan(), style, culture, out _)); Assert.ThrowsExactly(() => PreciseNumber.Parse(text, style, culture)); } + + [TestMethod] + [DataRow("3e1\0 ", NumberStyles.Float)] + [DataRow("3E0\0 ", NumberStyles.Any)] + [DataRow("3e1\0-", NumberStyles.Any)] + [DataRow("3e1\0\0-", NumberStyles.Any)] + [DataRow("3e1\0$", NumberStyles.Any)] + public void ParseRejectsANulInsideTheExponent(string text, NumberStyles style) + { + // Invariant, but with en-US's currency symbol, so "$" is read as one. + NumberFormatInfo culture = (NumberFormatInfo)NumberFormatInfo.InvariantInfo.Clone(); + culture.CurrencySymbol = "$"; + Assert.IsFalse(decimal.TryParse(text, style, culture, out _), "The case must be one decimal rejects."); + Assert.IsFalse(double.TryParse(text, style, culture, out _), "The case must be one double rejects."); + + Assert.IsFalse(PreciseNumber.TryParse(text, style, culture, out _)); + Assert.IsFalse(PreciseNumber.TryParse(text.AsSpan(), style, culture, out _)); + Assert.ThrowsExactly(() => PreciseNumber.Parse(text, style, culture)); + } } diff --git a/PreciseNumber/PreciseNumber.cs b/PreciseNumber/PreciseNumber.cs index 8d5ec22..6191744 100644 --- a/PreciseNumber/PreciseNumber.cs +++ b/PreciseNumber/PreciseNumber.cs @@ -1409,6 +1409,13 @@ private static PreciseNumber FromParsedParts(ReadOnlySpan digits, int deci long exponent = 0; if (hasExponent) { + // long.Parse ignores trailing NULs by itself, so a NUL the parse kept because something follows + // it, as in "3e1\0 " once the white space is trimmed, would otherwise slip through here. + if (exponentText.Contains('\0')) + { + throw new FormatException(InvalidFormatMessage); + } + // Only a sign may precede its digits: NumberStyles.Integer would also let white space through, // accepting "1e 5" where every BCL numeric type rejects it. A zero is zero whatever its written // exponent, as for decimal and double, so only a nonzero value can overflow here.