/// <summary> /// Modifies the initial estimate until the closest double-precision number to the desired /// value is found. /// </summary> /// <param name="initialEstimate"> The initial estimate. Assumed to be very close to the /// result. </param> /// <param name="base10Exponent"> The power-of-ten scale factor. </param> /// <param name="desiredValue"> The desired value, already scaled using the power-of-ten /// scale factor. </param> /// <returns> The closest double-precision number to the desired value. If there are two /// such values, the one with the least significant bit set to zero is returned. </returns> private static double RefineEstimate(double initialEstimate, int base10Exponent, BigInteger desiredValue) { // Numbers with 16 digits or more are tricky because rounding error can cause the // result to be out by one or more ULPs (units in the last place). // The algorithm is as follows: // 1. Use the initially calculated result as an estimate. // 2. Create a second estimate by modifying the estimate by one ULP. // 3. Calculate the actual value of both estimates to precision X (using arbitrary // precision arithmetic). // 4. If they are both above the desired value then decrease the estimates by 1 // ULP and goto step 3. // 5. If they are both below the desired value then increase up the estimates by // 1 ULP and goto step 3. // 6. One estimate must now be above the desired value and one below. // 7. If one is estimate is clearly closer to the desired value than the other, // then return that estimate. // 8. Increase the precision by 32 bits. // 9. If the precision is less than or equal to 160 bits goto step 3. // 10. Assume that the estimates are equally close to the desired value; return the // value with the least significant bit equal to 0. int direction = double.IsPositiveInfinity(initialEstimate) ? -1 : 1; int precision = 32; // Calculate the candidate value by modifying the last bit. double result = initialEstimate; double result2 = AddUlps(result, direction); // Figure out our multiplier. Either base10Exponent is positive, in which case we // multiply actual1 and actual2, or it's negative, in which case we multiply // desiredValue. BigInteger multiplier = BigInteger.One; if (base10Exponent < 0) { multiplier = BigInteger.Pow(10, -base10Exponent); } else if (base10Exponent > 0) { desiredValue = BigInteger.Multiply(desiredValue, BigInteger.Pow(10, base10Exponent)); } while (precision <= 160) { // Scale the candidate values to a big integer. var actual1 = ScaleToInteger(result, multiplier, precision); var actual2 = ScaleToInteger(result2, multiplier, precision); // Calculate the differences between the candidate values and the desired value. var baseline = BigInteger.LeftShift(desiredValue, precision); var diff1 = BigInteger.Subtract(actual1, baseline); var diff2 = BigInteger.Subtract(actual2, baseline); if (diff1.Sign == direction && diff2.Sign == direction) { // We're going the wrong way! direction = -direction; result2 = AddUlps(result, direction); } else if (diff1.Sign == -direction && diff2.Sign == -direction) { // Going the right way, but need to go further. result = result2; result2 = AddUlps(result, direction); } else { // Found two values that bracket the actual value. // If one candidate value is closer to the actual value by at least 2 (one // doesn't cut it because of the integer division) then use that value. diff1 = BigInteger.Abs(diff1); diff2 = BigInteger.Abs(diff2); if (BigInteger.Compare(diff1, BigInteger.Subtract(diff2, BigInteger.One)) < 0) { return(result); } if (BigInteger.Compare(diff2, BigInteger.Subtract(diff1, BigInteger.One)) < 0) { return(result2); } // Not enough precision to determine the correct answer, or it's a halfway case. // Increase the precision. precision += 32; } // If result2 is NaN then we have gone too far. if (double.IsNaN(result2)) { return(result); } } // Even with heaps of precision there is no clear winner. // Assume this is a halfway case: choose the floating-point value with its least // significant bit equal to 0. return((BitConverter.DoubleToInt64Bits(result) & 1) == 0 ? result : result2); }
/// <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, bool allowOctal) { 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 || allowOctal)) { // 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) { // Hex number. reader.Read(); result = ParseHex(reader); if (double.IsNaN(result)) { status = ParseCoreStatus.InvalidHexLiteral; return(double.NaN); } status = ParseCoreStatus.HexLiteral; return(result); } if (c >= '0' && c <= '9' && allowOctal) { // Octal number. result = ParseOctal(reader); if (double.IsNaN(result)) { 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 = (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); }