From 5fd78b21f5bcb3cd573980b6e4e84665ee434c3e Mon Sep 17 00:00:00 2001 From: TylerBrinkley Date: Mon, 29 Aug 2016 11:19:21 -0500 Subject: [PATCH] Made mantissa be stored in a ulong instead of double to prevent accumulating numeric error. Added case where if the negative exponent is less than 23 to divide from the positive exponents array to pass unit tests. --- .../Utilities/ConvertUtilsTests.cs | 4 +- Src/Newtonsoft.Json/Utilities/ConvertUtils.cs | 141 ++++++++++-------- 2 files changed, 80 insertions(+), 65 deletions(-) diff --git a/Src/Newtonsoft.Json.Tests/Utilities/ConvertUtilsTests.cs b/Src/Newtonsoft.Json.Tests/Utilities/ConvertUtilsTests.cs index 87bbca34..37e4b0b3 100644 --- a/Src/Newtonsoft.Json.Tests/Utilities/ConvertUtilsTests.cs +++ b/Src/Newtonsoft.Json.Tests/Utilities/ConvertUtilsTests.cs @@ -61,7 +61,7 @@ namespace Newtonsoft.Json.Tests.Utilities { Assert.AreEqual(expectedValue.Value, d, "Input string: " + s); - Assert.AreEqual(expectedValue, d2, "DoubleTryParse result is not equal to double.Parse. Input string: " + s); + Assert.AreEqual(expectedValue.Value, d2, "DoubleTryParse result is not equal to double.Parse. Input string: " + s); } } @@ -89,6 +89,8 @@ namespace Newtonsoft.Json.Tests.Utilities AssertDoubleTryParse("1", ParseResult.Success, 1); AssertDoubleTryParse("1.2", ParseResult.Success, 1.2); + AssertDoubleTryParse("1E-21", ParseResult.Success, 1E-21); + AssertDoubleTryParse("1E-22", ParseResult.Success, 1E-22); AssertDoubleTryParse("1E-23", ParseResult.Success, 1E-23); AssertDoubleTryParse("1E-25", ParseResult.Success, 1E-25); AssertDoubleTryParse("1E-50", ParseResult.Success, 1E-50); diff --git a/Src/Newtonsoft.Json/Utilities/ConvertUtils.cs b/Src/Newtonsoft.Json/Utilities/ConvertUtils.cs index 69461856..6790c5e6 100644 --- a/Src/Newtonsoft.Json/Utilities/ConvertUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/ConvertUtils.cs @@ -998,10 +998,13 @@ namespace Newtonsoft.Json.Utilities int numDecimalEnd = end; int exponent = 0; bool exponentNegative = false; + ulong mantissa = 0UL; + int mantissaDigits = 0; + int exponentFromMantissa = 0; for (; i < end; i++) { char c = chars[i]; - switch (chars[i]) + switch (c) { case '.': if (i == start) @@ -1019,87 +1022,89 @@ namespace Newtonsoft.Json.Utilities break; case 'e': case 'E': + if (i == start) { - if (i == start) - { - return ParseResult.Invalid; - } - if (i == numDecimalStart) - { - // E follows decimal point - return ParseResult.Invalid; - } - i++; - if (i == end) - { - return ParseResult.Invalid; - } + return ParseResult.Invalid; + } + if (i == numDecimalStart) + { + // E follows decimal point + return ParseResult.Invalid; + } + i++; + if (i == end) + { + return ParseResult.Invalid; + } - if (numDecimalStart < end) - { - numDecimalEnd = i - 1; - } + if (numDecimalStart < end) + { + numDecimalEnd = i - 1; + } + c = chars[i]; + switch (c) + { + case '-': + exponentNegative = true; + i++; + break; + case '+': + i++; + break; + } + + // parse 3 digit + for (;i < end;i++) + { c = chars[i]; - switch (c) + if (c < '0' || c > '9') { - case '-': - exponentNegative = true; - i++; - break; - case '+': - i++; - break; + return ParseResult.Invalid; } - // parse 3 digit - for (;i < end;i++) - { - c = chars[i]; - if (c < '0' || c > '9') - { - return ParseResult.Invalid; - } + exponent = (10 * exponent) + (c - '0'); + } - exponent = (10 * exponent) + (c - '0'); - } - - if (exponentNegative) + if (exponentNegative) + { + exponent = -exponent; + } + else + { + if (exponent > MaxExponent) { - exponent = -exponent; - } - else - { - if (exponent > MaxExponent) - { - return ParseResult.Overflow; - } + return ParseResult.Overflow; } } break; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - value = (10 * value) + (c - '0'); - break; default: - return ParseResult.Invalid; + if (c < '0' || c > '9') + { + return ParseResult.Invalid; + } + + if (mantissaDigits < 19) + { + mantissa = (10 * mantissa) + (ulong)(c - '0'); + ++mantissaDigits; + } + else + { + ++exponentFromMantissa; + } + break; } } + exponent += exponentFromMantissa; + // correct the decimal point exponent -= (numDecimalEnd - numDecimalStart); if (exponent > 0) { - value *= DoubleExponents[exponent - 1]; + value = mantissa * DoubleExponents[exponent - 1]; } else if (exponent < 0) { @@ -1107,17 +1112,25 @@ namespace Newtonsoft.Json.Utilities if (exponent > MaxExponent) { // handle very small numbers, e.g. 4.94065645841247E-324 - value *= DoubleNegativeExponents[154 - 1]; + value = mantissa * DoubleNegativeExponents[154 - 1]; exponent -= 154; } + else + { + value = mantissa; + } if (DoubleNegativeExponents.Length <= exponent - 1) { value = 0; return ParseResult.Success; } - - value *= DoubleNegativeExponents[exponent - 1]; + + value = exponent < 23 ? value / DoubleExponents[exponent - 1] : value * DoubleNegativeExponents[exponent - 1]; + } + else + { + value = mantissa; } if (double.IsPositiveInfinity(value))