/// <summary>
        /// Converts a string to an integer (used by parseInt).
        /// </summary>
        /// <param name="input">The input string.</param>
        /// <param name="radix"> The numeric base to use for parsing.  Pass zero to use base 10
        /// except when the input string starts with '0' in which case base 16 or base 8 are used
        /// instead. </param>
        /// <param name="allowOctal"> <c>true</c> if numbers with a leading zero should be parsed
        /// as octal numbers. </param>
        /// <returns> The result of parsing the string as a integer. </returns>
        internal static double ParseInt(string input, int radix, bool allowOctal)
        {
            var reader     = new System.IO.StringReader(input);
            int digitCount = 0;

            // Skip whitespace and line terminators.
            while (IsWhiteSpaceOrLineTerminator(reader.Peek()))
            {
                reader.Read();
            }

            // Determine the sign.
            double sign = 1;

            if (reader.Peek() == '+')
            {
                reader.Read();
            }
            else if (reader.Peek() == '-')
            {
                sign = -1;
                reader.Read();
            }

            // Hex prefix should be stripped if the radix is 0, undefined or 16.
            bool stripPrefix = radix == 0 || radix == 16;

            // Default radix is 10.
            if (radix == 0)
            {
                radix = 10;
            }

            // Skip past the prefix, if there is one.
            if (stripPrefix)
            {
                if (reader.Peek() == '0')
                {
                    reader.Read();
                    digitCount = 1; // Note: required for parsing "0z11" correctly (when radix = 0).

                    int c = reader.Peek();
                    if (c == 'x' || c == 'X')
                    {
                        // Hex number.
                        reader.Read();
                        radix = 16;
                    }

                    if (c >= '0' && c <= '9' && allowOctal)
                    {
                        // Octal number.
                        radix = 8;
                    }
                }
            }

            // Calculate the maximum number of digits before arbitrary precision arithmetic is
            // required.
            int maxDigits = (int)Math.Floor(53 / Math.Log(radix, 2));

            // Read numeric digits 0-9, a-z or A-Z.
            double result    = 0;
            var    bigResult = BigInteger.Zero;

            while (true)
            {
                int numericValue = -1;
                int c            = reader.Read();
                if (c >= '0' && c <= '9')
                {
                    numericValue = c - '0';
                }
                if (c >= 'a' && c <= 'z')
                {
                    numericValue = c - 'a' + 10;
                }
                if (c >= 'A' && c <= 'Z')
                {
                    numericValue = c - 'A' + 10;
                }
                if (numericValue == -1 || numericValue >= radix)
                {
                    break;
                }
                if (digitCount == maxDigits)
                {
                    bigResult = BigInteger.FromDouble(result);
                }
                result = result * radix + numericValue;
                if (digitCount >= maxDigits)
                {
                    bigResult = BigInteger.MultiplyAdd(bigResult, radix, numericValue);
                }
                digitCount++;
            }

            // If the input is empty, then return NaN.
            if (digitCount == 0)
            {
                return(double.NaN);
            }

            // Numbers with lots of digits require the use of arbitrary precision arithmetic to
            // determine the correct answer.
            if (digitCount > maxDigits)
            {
                return(RefineEstimate(result, 0, bigResult) * sign);
            }

            return(result * sign);
        }
Exemple #2
0
        /// <summary>
        /// Converts a number to a string.
        /// </summary>
        /// <param name="value"> The value to convert to a string. </param>
        /// <param name="radix"> The base of the number system to convert to. </param>
        /// <param name="numberFormatInfo"> The number format style to use. </param>
        /// <param name="style"> The type of formatting to apply. </param>
        /// <param name="precision">
        /// This value is dependent on the formatting style:
        /// Regular - this value has no meaning.
        /// Precision - the number of significant figures to display.
        /// Fixed - the number of figures to display after the decimal point.
        /// Exponential - the number of figures to display after the decimal point.
        /// </param>
        internal static string ToString(double value, int radix, System.Globalization.NumberFormatInfo numberFormatInfo, Style style, int precision)
        {
            // Handle NaN.
            if (double.IsNaN(value))
            {
                return(numberFormatInfo.NaNSymbol); // "NaN"
            }
            // Handle zero.
            if (Math.Abs(value - 0.0) < Double.Epsilon)
            {
                switch (style)
                {
                case Style.Regular:
                    return("0");

                case Style.Precision:
                    return("0" + numberFormatInfo.NumberDecimalSeparator + new string('0', precision - 1));

                case Style.Fixed:
                    if (precision == 0)
                    {
                        return("0");
                    }
                    return("0" + numberFormatInfo.NumberDecimalSeparator + new string('0', precision));

                case Style.Exponential:
                    if (precision <= 0)
                    {
                        return("0" + ExponentSymbol + numberFormatInfo.PositiveSign + "0");
                    }
                    return("0" + numberFormatInfo.NumberDecimalSeparator + new string('0', precision) + ExponentSymbol + numberFormatInfo.PositiveSign + "0");
                }
            }

            var result = new System.Text.StringBuilder(18);

            // Handle negative numbers.
            if (value < 0.0)
            {
                value = -value;
                result.Append(numberFormatInfo.NegativeSign);
            }

            // Handle infinity.
            if (double.IsPositiveInfinity(value))
            {
                result.Append(numberFormatInfo.PositiveInfinitySymbol); // "Infinity"
                return(result.ToString());
            }

            // Extract the base-2 exponent.
            var bits = new DoubleBits {
                DoubleValue = value
            };
            int base2Exponent = (int)(bits.LongValue >> MantissaExplicitBits);

            // Extract the mantissa.
            long mantissa = bits.LongValue & MantissaMask;

            // Correct the base-2 exponent.
            if (base2Exponent == 0)
            {
                // This is a denormalized number.
                base2Exponent = base2Exponent - ExponentBias - MantissaExplicitBits + 1;
            }
            else
            {
                // This is a normal number.
                base2Exponent = base2Exponent - ExponentBias - MantissaExplicitBits;

                // Add the implicit bit.
                mantissa |= MantissaImplicitBit;
            }

            // Remove any trailing zeros.
            int trailingZeroBits = CountTrailingZeroBits((ulong)mantissa);

            mantissa     >>= trailingZeroBits;
            base2Exponent += trailingZeroBits;

            // Calculate the logarithm of the number.
            int exponent;

            if (radix == 10)
            {
                exponent = (int)Math.Floor(Math.Log10(value));

                // We need to calculate k = floor(log10(x)).
                // log(x)	~=~ log(1.5) + (x-1.5)/1.5 (taylor series approximation)
                // log10(x) ~=~ log(1.5) / log(10) + (x - 1.5) / (1.5 * log(10))
                // d = x * 2^l (1 <= x < 2)
                // log10(d) = l * log10(2) + log10(x)
                // log10(d) ~=~ l * log10(2)           + (x - 1.5) * (1 / (1.5 * log(10)))  + log(1.5) / log(10)
                // log10(d) ~=~ l * 0.301029995663981  + (x - 1.5) * 0.289529654602168      + 0.1760912590558
                // The last term (0.1760912590558) is rounded so that k = floor(log10(x)) or
                // k = floor(log10(x)) + 1 (i.e. it's the exact value or one higher).


                //double log10;
                //if ((int)(bits.LongValue >> MantissaExplicitBits) == 0)
                //{
                //    // The number is denormalized.
                //    int mantissaShift = CountLeadingZeroBits((ulong)mantissa) - (64 - MantissaImplicitBits);
                //    bits.LongValue = (mantissa << mantissaShift) & MantissaMask |
                //        ((long)ExponentBias << MantissaExplicitBits);

                //    // Calculate an overestimate of log-10 of the value.
                //    log10 = (bits.DoubleValue - 1.5) * 0.289529654602168 + 0.1760912590558 +
                //        (base2Exponent - mantissaShift) * 0.301029995663981;
                //}
                //else
                //{
                //    // Set the base-2 exponent to biased zero.
                //    bits.LongValue = (bits.LongValue & ~ExponentMask) | ((long)ExponentBias << MantissaExplicitBits);

                //    // Calculate an overestimate of log-10 of the value.
                //    log10 = (bits.DoubleValue - 1.5) * 0.289529654602168 + 0.1760912590558 + base2Exponent * 0.301029995663981;
                //}

                //// (int)Math.Floor(log10)
                //exponent = (int)log10;
                //if (log10 < 0 && log10 != exponent)
                //    exponent--;

                //if (exponent >= 0 && exponent < tens.Length)
                //{
                //    if (value < tens[exponent])
                //        exponent--;
                //}
            }
            else
            {
                exponent = (int)Math.Floor(Math.Log(value, radix));
            }

            if (radix == 10 && style == Style.Regular)
            {
                // Do we have a small integer?
                if (base2Exponent >= 0 && exponent <= 14)
                {
                    // Yes.
                    for (int i = exponent; i >= 0; i--)
                    {
                        double scaleFactor = Tens[i];
                        int    digit       = (int)(value / scaleFactor);
                        result.Append((char)(digit + '0'));
                        value -= digit * scaleFactor;
                    }
                    return(result.ToString());
                }
            }

            // toFixed acts like toString() if the exponent is >= 21.
            if (style == Style.Fixed && exponent >= 21)
            {
                style = Style.Regular;
            }

            // Calculate the exponent thresholds.
            int lowExponentThreshold = int.MinValue;

            if (radix == 10 && style != Style.Fixed)
            {
                lowExponentThreshold = -7;
            }
            if (style == Style.Exponential)
            {
                lowExponentThreshold = -1;
            }
            int highExponentThreshold = int.MaxValue;

            if (radix == 10 && style == Style.Regular)
            {
                highExponentThreshold = 21;
            }
            if (style == Style.Precision)
            {
                highExponentThreshold = precision;
            }
            if (style == Style.Exponential)
            {
                highExponentThreshold = 0;
            }

            // Calculate the number of bits per digit.
            double bitsPerDigit = radix == 10 ? 3.322 : Math.Log(radix, 2);

            // Calculate the maximum number of digits to output.
            // We add 7 so that there is enough precision to distinguish halfway numbers.
            int maxDigitsToOutput = radix == 10 ? 22 : (int)Math.Floor(53 / bitsPerDigit) + 7;

            // Calculate the number of integral digits, or if negative, the number of zeros after
            // the decimal point.
            int integralDigits = exponent + 1;

            // toFixed with a low precision causes rounding.
            if (style == Style.Fixed && precision <= -integralDigits)
            {
                int diff = (-integralDigits) - (precision - 1);
                maxDigitsToOutput += diff;
                exponent          += diff;
                integralDigits    += diff;
            }

            // Output any leading zeros.
            bool decimalPointOutput = false;

            if (integralDigits <= 0 && integralDigits > lowExponentThreshold + 1)
            {
                result.Append('0');
                if (integralDigits < 0)
                {
                    result.Append(numberFormatInfo.NumberDecimalSeparator);
                    decimalPointOutput = true;
                    result.Append('0', -integralDigits);
                }
            }

            // We need to calculate the integers "scaledValue" and "divisor" such that:
            // value = scaledValue / divisor * 10 ^ exponent
            // 1 <= scaledValue / divisor < 10

            BigInteger scaledValue = new BigInteger(mantissa);
            BigInteger divisor     = BigInteger.One;
            BigInteger multiplier  = BigInteger.One;

            if (exponent > 0)
            {
                // Number is >= 10.
                divisor = BigInteger.Multiply(divisor, BigInteger.Pow(radix, exponent));
            }
            else if (exponent < 0)
            {
                // Number is < 1.
                multiplier  = BigInteger.Pow(radix, -exponent);
                scaledValue = BigInteger.Multiply(scaledValue, multiplier);
            }

            // Scale the divisor so it is 74 bits ((21 digits + 1 digit for rounding) * approx 3.322 bits per digit).
            int powerOfTwoScaleFactor = (radix == 10 ? 74 : (int)Math.Ceiling(maxDigitsToOutput * bitsPerDigit)) - divisor.BitCount;

            divisor     = BigInteger.LeftShift(divisor, powerOfTwoScaleFactor);
            scaledValue = BigInteger.LeftShift(scaledValue, powerOfTwoScaleFactor + base2Exponent);

            // Calculate the error.
            BigInteger errorDelta      = BigInteger.Zero;
            int        errorPowerOfTen = int.MinValue;

            switch (style)
            {
            case Style.Regular:
                errorDelta = ScaleToInteger(CalculateError(value), multiplier, powerOfTwoScaleFactor - 1);
                break;

            case Style.Precision:
                errorPowerOfTen = integralDigits - precision;
                break;

            case Style.Fixed:
                errorPowerOfTen = -precision;
                break;

            case Style.Exponential:
                if (precision < 0)
                {
                    errorDelta = ScaleToInteger(CalculateError(value), multiplier, powerOfTwoScaleFactor - 1);
                }
                else
                {
                    errorPowerOfTen = integralDigits - precision - 1;
                }
                break;

            default:
                throw new ArgumentException(@"Unknown formatting style.", "style");
            }
            if (errorPowerOfTen != int.MinValue)
            {
                errorDelta = multiplier;
                if (errorPowerOfTen > 0)
                {
                    errorDelta = BigInteger.Multiply(errorDelta, BigInteger.Pow(radix, errorPowerOfTen));
                }
                errorDelta = BigInteger.LeftShift(errorDelta, powerOfTwoScaleFactor - 1);
                if (errorPowerOfTen < 0)
                {
                    // We would normally divide by the power of 10 here, but division is extremely
                    // slow so we multiply everything else instead.
                    //errorDelta = BigInteger.Divide(errorDelta, BigInteger.Pow(radix, -errorPowerOfTen));
                    var errorPowerOfTenMultiplier = BigInteger.Pow(radix, -errorPowerOfTen);
                    scaledValue = BigInteger.Multiply(scaledValue, errorPowerOfTenMultiplier);
                    divisor     = BigInteger.Multiply(divisor, errorPowerOfTenMultiplier);
                    BigInteger.SetupQuorum(ref scaledValue, ref divisor, ref errorDelta);
                }
            }

            // Shrink the error in the case where ties are resolved towards the value with the
            // least significant bit set to zero.
            if ((BitConverter.DoubleToInt64Bits(value) & 1) == 1)
            {
                errorDelta.InPlaceDecrement();
            }

            // Cache half the divisor.
            BigInteger halfDivisor = BigInteger.RightShift(divisor, 1);

            // Output the digits.
            int  zeroCount = 0;
            int  digitsOutput = 0;
            bool rounded = false, scientificNotation = false;

            for (; digitsOutput < maxDigitsToOutput && rounded == false; digitsOutput++)
            {
                // Calculate the next digit.
                var digit = BigInteger.Quorem(ref scaledValue, divisor);

                if (BigInteger.Compare(scaledValue, errorDelta) <= 0 && BigInteger.Compare(scaledValue, halfDivisor) < 0)
                {
                    // Round down.
                    rounded = true;
                }
                else if (BigInteger.Compare(BigInteger.Subtract(divisor, scaledValue), errorDelta) <= 0)
                {
                    // Round up.
                    rounded = true;
                    digit++;
                    if (digit == radix)
                    {
                        digit = 1;
                        exponent++;
                        integralDigits++;
                    }
                }

                if (digit > 0 || decimalPointOutput == false)
                {
                    // Check if the decimal point should be output.
                    if (decimalPointOutput == false && (scientificNotation || digitsOutput == integralDigits))
                    {
                        result.Append(numberFormatInfo.NumberDecimalSeparator);
                        decimalPointOutput = true;
                    }

                    // Output any pent-up zeros.
                    if (zeroCount > 0)
                    {
                        result.Append('0', zeroCount);
                        zeroCount = 0;
                    }

                    // Output the next digit.
                    if (digit < 10)
                    {
                        result.Append((char)(digit + '0'));
                    }
                    else
                    {
                        result.Append((char)(digit - 10 + 'a'));
                    }
                }
                else
                {
                    zeroCount++;
                }

                // Check whether the number should be displayed in scientific notation (we cannot
                // determine this up front for large exponents because the number might get rounded
                // up to cross the threshold).
                if (digitsOutput == 0 && (exponent <= lowExponentThreshold || exponent >= highExponentThreshold))
                {
                    scientificNotation = true;
                }

                scaledValue = BigInteger.MultiplyAdd(scaledValue, radix, 0);
                errorDelta  = BigInteger.MultiplyAdd(errorDelta, radix, 0);
            }

            // Add any extra zeros on the end, if necessary.
            if (scientificNotation == false && integralDigits > digitsOutput)
            {
                result.Append('0', integralDigits - digitsOutput);
                digitsOutput = integralDigits;
            }

            // Most of the styles output redundent zeros.
            int redundentZeroCount = 0;

            switch (style)
            {
            case Style.Precision:
                redundentZeroCount = zeroCount + precision - digitsOutput;
                break;

            case Style.Fixed:
                redundentZeroCount = precision - (digitsOutput - zeroCount - integralDigits);
                break;

            case Style.Exponential:
                redundentZeroCount = precision - (digitsOutput - zeroCount) + 1;
                break;
            }
            if (redundentZeroCount > 0)
            {
                if (decimalPointOutput == false)
                {
                    result.Append(numberFormatInfo.NumberDecimalSeparator);
                }
                result.Append('0', redundentZeroCount);
            }

            if (scientificNotation)
            {
                // Add the exponent on the end.
                result.Append(ExponentSymbol);
                if (exponent > 0)
                {
                    result.Append(numberFormatInfo.PositiveSign);
                }
                result.Append(exponent);
            }

            return(result.ToString());
        }
        /// <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);
        }