/// <summary> /// Parses a number and returns the corresponding double-precision value. /// </summary> /// <param name="reader"> The reader to read characters from. </param> /// <param name="firstChar"> The first character of the number. Must be 0-9 or a period. </param> /// <param name="status"> Upon returning, contains the type of error if one occurred. </param> /// <param name="allowHex"> </param> /// <param name="allowOctal"> </param> /// <returns> The numeric value, or <c>NaN</c> if the number is invalid. </returns> internal static double ParseCore(TextReader reader, char firstChar, out ParseCoreStatus status, bool allowHex = true, bool allowOctal = true) { double result; // A count of the number of integral and fractional digits of the input number. int totalDigits = 0; // Assume success. status = ParseCoreStatus.Success; // If the number starts with '0' then the number is a hex literal or a octal literal. if (firstChar == '0' && (allowHex == true || allowOctal == true)) { // Read the next char - should be 'x' or 'X' if this is a hex number (could be just '0'). int c = reader.Peek(); if ((c == 'x' || c == 'X') && allowHex == true) { // Hex number. reader.Read(); result = ParseHex(reader); if (double.IsNaN(result) == true) { status = ParseCoreStatus.InvalidHexLiteral; return double.NaN; } status = ParseCoreStatus.HexLiteral; return result; } else if (c >= '0' && c <= '9' && allowOctal == true) { // Octal number. result = ParseOctal(reader); if (double.IsNaN(result) == true) { status = ParseCoreStatus.InvalidOctalLiteral; return double.NaN; } status = ParseCoreStatus.OctalLiteral; return result; } } // desired1-3 hold the integral and fractional digits of the input number. // desired1 holds the first set of nine digits, desired2 holds the second set of nine // digits, desired3 holds the rest. int desired1 = 0; int desired2 = 0; var desired3 = BigInteger.Zero; // Indicates the base-10 scale factor of the output e.g. the result is // desired x 10^exponentBase10. int exponentBase10 = 0; // Read the integer component. if (firstChar >= '0' && firstChar <= '9') { desired1 = firstChar - '0'; totalDigits = 1; while (true) { int c = reader.Peek(); if (c < '0' || c > '9') break; reader.Read(); if (totalDigits < 9) desired1 = desired1 * 10 + (c - '0'); else if (totalDigits < 18) desired2 = desired2 * 10 + (c - '0'); else desired3 = BigInteger.MultiplyAdd(desired3, 10, c - '0'); totalDigits++; } } if (firstChar == '.' || reader.Peek() == '.') { // Skip past the period. if (firstChar != '.') reader.Read(); // Read the fractional component. int fractionalDigits = 0; while (true) { int c = reader.Peek(); if (c < '0' || c > '9') break; reader.Read(); if (totalDigits < 9) desired1 = desired1 * 10 + (c - '0'); else if (totalDigits < 18) desired2 = desired2 * 10 + (c - '0'); else desired3 = BigInteger.MultiplyAdd(desired3, 10, c - '0'); totalDigits++; fractionalDigits++; exponentBase10--; } // Check if the number consists solely of a period. if (totalDigits == 0) { status = ParseCoreStatus.NoDigits; return double.NaN; } // Check if the number has a period but no digits afterwards. if (fractionalDigits == 0) status = ParseCoreStatus.NoFraction; } if (reader.Peek() == 'e' || reader.Peek() == 'E') { // Skip past the 'e'. reader.Read(); // Read the sign of the exponent. bool exponentNegative = false; int c = reader.Peek(); if (c == '+') reader.Read(); else if (c == '-') { reader.Read(); exponentNegative = true; } // Read the first character of the exponent. int firstExponentChar = reader.Read(); // Check there is a number after the 'e'. int exponent = 0; if (firstExponentChar < '0' || firstExponentChar > '9') { status = ParseCoreStatus.NoExponent; } else { // Read the rest of the exponent. exponent = firstExponentChar - '0'; int exponentDigits = 1; while (true) { c = reader.Peek(); if (c < '0' || c > '9') break; reader.Read(); exponent = Math.Min(exponent * 10 + (c - '0'), 9999); exponentDigits++; } // JSON does not allow a leading zero in front of the exponent. if (firstExponentChar == '0' && exponentDigits > 1 && status == ParseCoreStatus.Success) status = ParseCoreStatus.ExponentHasLeadingZero; } // Keep track of the overall base-10 exponent. exponentBase10 += exponentNegative ? -exponent : exponent; } // Calculate the integral and fractional portion of the number, scaled to an integer. if (totalDigits < 16) { // Combine desired1 and desired2 to produce an integer representing the final // result. result = (double)((long)desired1 * integerPowersOfTen[Math.Max(totalDigits - 9, 0)] + desired2); } else { // Combine desired1, desired2 and desired3 to produce an integer representing the // final result. var temp = desired3; desired3 = new BigInteger((long)desired1 * integerPowersOfTen[Math.Min(totalDigits - 9, 9)] + desired2); if (totalDigits > 18) { desired3 = BigInteger.Multiply(desired3, BigInteger.Pow(10, totalDigits - 18)); desired3 = BigInteger.Add(desired3, temp); } result = desired3.ToDouble(); } // Apply the base-10 exponent. if (exponentBase10 > 0) result *= Math.Pow(10, exponentBase10); else if (exponentBase10 < 0 && exponentBase10 >= -308) result /= Math.Pow(10, -exponentBase10); else if (exponentBase10 < -308) { // Note: 10^308 is the largest representable power of ten. result /= Math.Pow(10, 308); result /= Math.Pow(10, -exponentBase10 - 308); } // Numbers with 16 or more digits require the use of arbitrary precision arithmetic to // determine the correct answer. if (totalDigits >= 16) return RefineEstimate(result, exponentBase10, desired3); return result; }
internal static double ParseCore(TextReader reader, char firstChar, out ParseCoreStatus status, bool decimalOnly) { return(ParseCore(reader, firstChar, out status, decimalOnly, true)); }
/// <summary> /// Parses a number and returns the corresponding double-precision value. /// </summary> /// <param name="reader"> The reader to read characters from. </param> /// <param name="firstChar"> The first character of the number. Must be 0-9 or a period. </param> /// <param name="status"> Upon returning, contains the type of error if one occurred. </param> /// <param name="allowHex"> </param> /// <param name="allowOctal"> </param> /// <returns> The numeric value, or <c>NaN</c> if the number is invalid. </returns> internal static double ParseCore(TextReader reader, char firstChar, out ParseCoreStatus status, bool allowHex = true, bool allowOctal = true) { double result; // A count of the number of integral and fractional digits of the input number. int totalDigits = 0; // Assume success. status = ParseCoreStatus.Success; // If the number starts with '0' then the number is a hex literal or a octal literal. if (firstChar == '0' && (allowHex == true || allowOctal == true)) { // Read the next char - should be 'x' or 'X' if this is a hex number (could be just '0'). int c = reader.Peek(); if ((c == 'x' || c == 'X') && allowHex == true) { // Hex number. reader.Read(); result = ParseHex(reader); if (double.IsNaN(result) == true) { status = ParseCoreStatus.InvalidHexLiteral; return(double.NaN); } status = ParseCoreStatus.HexLiteral; return(result); } else if (c >= '0' && c <= '9' && allowOctal == true) { // Octal number. result = ParseOctal(reader); if (double.IsNaN(result) == true) { status = ParseCoreStatus.InvalidOctalLiteral; return(double.NaN); } status = ParseCoreStatus.OctalLiteral; return(result); } } // desired1-3 hold the integral and fractional digits of the input number. // desired1 holds the first set of nine digits, desired2 holds the second set of nine // digits, desired3 holds the rest. int desired1 = 0; int desired2 = 0; var desired3 = BigInteger.Zero; // Indicates the base-10 scale factor of the output e.g. the result is // desired x 10^exponentBase10. int exponentBase10 = 0; // Read the integer component. if (firstChar >= '0' && firstChar <= '9') { desired1 = firstChar - '0'; totalDigits = 1; while (true) { int c = reader.Peek(); if (c < '0' || c > '9') { break; } reader.Read(); if (totalDigits < 9) { desired1 = desired1 * 10 + (c - '0'); } else if (totalDigits < 18) { desired2 = desired2 * 10 + (c - '0'); } else { desired3 = BigInteger.MultiplyAdd(desired3, 10, c - '0'); } totalDigits++; } } if (firstChar == '.' || reader.Peek() == '.') { // Skip past the period. if (firstChar != '.') { reader.Read(); } // Read the fractional component. int fractionalDigits = 0; while (true) { int c = reader.Peek(); if (c < '0' || c > '9') { break; } reader.Read(); if (totalDigits < 9) { desired1 = desired1 * 10 + (c - '0'); } else if (totalDigits < 18) { desired2 = desired2 * 10 + (c - '0'); } else { desired3 = BigInteger.MultiplyAdd(desired3, 10, c - '0'); } totalDigits++; fractionalDigits++; exponentBase10--; } // Check if the number consists solely of a period. if (totalDigits == 0) { status = ParseCoreStatus.NoDigits; return(double.NaN); } // Check if the number has a period but no digits afterwards. if (fractionalDigits == 0) { status = ParseCoreStatus.NoFraction; } } if (reader.Peek() == 'e' || reader.Peek() == 'E') { // Skip past the 'e'. reader.Read(); // Read the sign of the exponent. bool exponentNegative = false; int c = reader.Peek(); if (c == '+') { reader.Read(); } else if (c == '-') { reader.Read(); exponentNegative = true; } // Read the first character of the exponent. int firstExponentChar = reader.Read(); // Check there is a number after the 'e'. int exponent = 0; if (firstExponentChar < '0' || firstExponentChar > '9') { status = ParseCoreStatus.NoExponent; } else { // Read the rest of the exponent. exponent = firstExponentChar - '0'; int exponentDigits = 1; while (true) { c = reader.Peek(); if (c < '0' || c > '9') { break; } reader.Read(); exponent = Math.Min(exponent * 10 + (c - '0'), 9999); exponentDigits++; } // JSON does not allow a leading zero in front of the exponent. if (firstExponentChar == '0' && exponentDigits > 1 && status == ParseCoreStatus.Success) { status = ParseCoreStatus.ExponentHasLeadingZero; } } // Keep track of the overall base-10 exponent. exponentBase10 += exponentNegative ? -exponent : exponent; } // Calculate the integral and fractional portion of the number, scaled to an integer. if (totalDigits < 16) { // Combine desired1 and desired2 to produce an integer representing the final // result. result = (double)((long)desired1 * integerPowersOfTen[Math.Max(totalDigits - 9, 0)] + desired2); } else { // Combine desired1, desired2 and desired3 to produce an integer representing the // final result. var temp = desired3; desired3 = new BigInteger((long)desired1 * integerPowersOfTen[Math.Min(totalDigits - 9, 9)] + desired2); if (totalDigits > 18) { desired3 = BigInteger.Multiply(desired3, BigInteger.Pow(10, totalDigits - 18)); desired3 = BigInteger.Add(desired3, temp); } result = desired3.ToDouble(); } // Apply the base-10 exponent. if (exponentBase10 > 0) { result *= Math.Pow(10, exponentBase10); } else if (exponentBase10 < 0 && exponentBase10 >= -308) { result /= Math.Pow(10, -exponentBase10); } else if (exponentBase10 < -308) { // Note: 10^308 is the largest representable power of ten. result /= Math.Pow(10, 308); result /= Math.Pow(10, -exponentBase10 - 308); } // Numbers with 16 or more digits require the use of arbitrary precision arithmetic to // determine the correct answer. if (totalDigits >= 16) { return(RefineEstimate(result, exponentBase10, desired3)); } return(result); }
internal static double ParseCore(TextReader reader, char firstChar, out ParseCoreStatus status) { return(ParseCore(reader, firstChar, out status, false, true)); }