private static Boolean HexNumberToInt64(ref NumberBuffer number, ref Int64 value)
 {
     UInt64 passedValue = 0;
     Boolean returnValue = HexNumberToUInt64(ref number, ref passedValue);
     value = (Int64)passedValue;
     return returnValue;
 }
 private static bool HexNumberToInt32(ref NumberBuffer number, ref int value)
 {
     uint num = 0;
     bool flag = HexNumberToUInt32(ref number, ref num);
     value = (int) num;
     return flag;
 }
 private static bool HexNumberToInt64(ref NumberBuffer number, ref long value)
 {
     ulong num = 0L;
     bool flag = HexNumberToUInt64(ref number, ref num);
     value = (long) num;
     return flag;
 }
            private static unsafe void DoubleToNumber(double value, int precision, ref NumberBuffer number)
            {
                number.precision = precision;
                if (DoubleHelper.Exponent(value) == 0x7ff)
                {
                    number.scale = DoubleHelper.Mantissa(value) != 0 ? SCALE_NAN : SCALE_INF;
                    number.sign = DoubleHelper.Sign(value);
                    number.digits[0] = '\0';
                }
                else
                {
                    byte* src = stackalloc byte[_CVTBUFSIZE];
                    int sign;
                    fixed (NumberBuffer* pNumber = &number)
                    {
                        RuntimeImports._ecvt_s(src, _CVTBUFSIZE, value, precision, &pNumber->scale, &sign);
                    }
                    number.sign = sign != 0;

                    char* dst = number.digits;
                    if ((char)*src != '0')
                    {
                        while (*src != 0)
                            *dst++ = (char)*src++;
                    }
                    *dst = '\0';
                }
            }
            private static unsafe void DoubleToNumber(double value, int precision, ref NumberBuffer number)
            {
                number.precision = precision;
                if (DoubleHelper.Exponent(value) == 0x7ff)
                {
                    number.scale     = DoubleHelper.Mantissa(value) != 0 ? SCALE_NAN : SCALE_INF;
                    number.sign      = DoubleHelper.Sign(value);
                    number.digits[0] = '\0';
                }
                else
                {
                    byte *src = stackalloc byte[_CVTBUFSIZE];
                    int   sign;
                    fixed(NumberBuffer *pNumber = &number)
                    {
                        RuntimeImports._ecvt_s(src, _CVTBUFSIZE, value, precision, &pNumber->scale, &sign);
                    }

                    number.sign = sign != 0;

                    char *dst = number.digits;
                    if ((char)*src != '0')
                    {
                        while (*src != 0)
                        {
                            *dst++ = (char)*src++;
                        }
                    }
                    *dst = '\0';
                }
            }
            //public static bool TryRunHalf(Half value, int requestedDigits, ref NumberBuffer number)
            //{
            //    Half v = Half.IsNegative(value) ? Half.Negate(value) : value;

            //    Debug.Assert((double)v > 0);
            //    Debug.Assert(Half.IsFinite(v));

            //    int length;
            //    int decimalExponent;
            //    bool result;

            //    if (requestedDigits == -1)
            //    {
            //        DiyFp w = DiyFp.CreateAndGetBoundaries(v, out DiyFp boundaryMinus, out DiyFp boundaryPlus).Normalize();
            //        result = TryRunShortest(in boundaryMinus, in w, in boundaryPlus, number.Digits, out length, out decimalExponent);
            //    }
            //    else
            //    {
            //        DiyFp w = new DiyFp(v).Normalize();
            //        result = TryRunCounted(in w, requestedDigits, number.Digits, out length, out decimalExponent);
            //    }

            //    if (result)
            //    {
            //        Debug.Assert((requestedDigits == -1) || (length == requestedDigits));

            //        number.Scale = length + decimalExponent;
            //        number.Digits[length] = (byte)('\0');
            //        number.DigitsCount = length;
            //    }

            //    return result;
            //}

            public static bool TryRunSingle(float value, int requestedDigits, ref NumberBuffer number)
            {
                var v = value.IsNegative() ? -value : value;

                Debug.Assert(v > 0);
                Debug.Assert(v.IsFinite());

                int  length;
                int  decimalExponent;
                bool result;

                if (requestedDigits == -1)
                {
                    var w = DiyFp.CreateAndGetBoundaries(v, out var boundaryMinus, out var boundaryPlus).Normalize();
                    result = TryRunShortest(in boundaryMinus, in w, in boundaryPlus, number.DigitsMut, out length, out decimalExponent);
                }
                else
                {
                    var w = new DiyFp(v).Normalize();
                    result = TryRunCounted(in w, requestedDigits, number.DigitsMut, out length, out decimalExponent);
                }

                if (result)
                {
                    Debug.Assert(requestedDigits == -1 || length == requestedDigits);

                    number.Scale             = length + decimalExponent;
                    number.DigitsMut[length] = (byte)'\0';
                    number.DigitsCount       = length;
                }

                return(result);
            }
Exemple #7
0
        private static Boolean HexNumberToInt32(ref NumberBuffer number, ref Int32 value)
        {
            UInt32  passedValue = 0;
            Boolean returnValue = HexNumberToUInt32(ref number, ref passedValue);

            value = (Int32)passedValue;
            return(returnValue);
        }
Exemple #8
0
        private unsafe static Boolean HexNumberToUInt32(ref NumberBuffer number, ref UInt32 value)
        {
            Int32 i = number.scale;

            if (i > UInt32Precision || i < number.precision)
            {
                return(false);
            }
            Char *p = number.digits;

            Debug.Assert(p != null, "");

            UInt32 n = 0;

            while (--i >= 0)
            {
                if (n > ((UInt32)0xFFFFFFFF / 16))
                {
                    return(false);
                }
                n *= 16;
                if (*p != '\0')
                {
                    UInt32 newN = n;
                    if (*p != '\0')
                    {
                        if (*p >= '0' && *p <= '9')
                        {
                            newN += (UInt32)(*p - '0');
                        }
                        else
                        {
                            if (*p >= 'A' && *p <= 'F')
                            {
                                newN += (UInt32)((*p - 'A') + 10);
                            }
                            else
                            {
                                Debug.Assert(*p >= 'a' && *p <= 'f', "");
                                newN += (UInt32)((*p - 'a') + 10);
                            }
                        }
                        p++;
                    }

                    // Detect an overflow here...
                    if (newN < n)
                    {
                        return(false);
                    }
                    n = newN;
                }
            }
            value = n;
            return(true);
        }
Exemple #9
0
        /// <summary>
        /// Parses a Decimal at the start of a Utf8 string.
        /// </summary>
        /// <param name="text">The Utf8 string to parse</param>
        /// <param name="value">Receives the parsed value</param>
        /// <param name="bytesConsumed">On a successful parse, receives the length in bytes of the substring that was parsed </param>
        /// <param name="standardFormat">Expected format of the Utf8 string</param>
        /// <returns>
        /// true for success. "bytesConsumed" contains the length in bytes of the substring that was parsed.
        /// false if the string was not syntactically valid or an overflow or underflow occurred. "bytesConsumed" is set to 0.
        /// </returns>
        /// <remarks>
        /// Formats supported:
        ///     G/g  (default)
        ///     F/f             12.45       Fixed point
        ///     E/e             1.245000e1  Exponential
        /// </remarks>
        /// <exceptions>
        /// <cref>System.FormatException</cref> if the format is not valid for this data type.
        /// </exceptions>
        public static bool TryParse(ReadOnlySpan <byte> text, out decimal value, out int bytesConsumed, char standardFormat = default)
        {
            ParseNumberOptions options;

            switch (standardFormat)
            {
            case (default(char)):
            case 'G':
            case 'g':
            case 'E':
            case 'e':
                options = ParseNumberOptions.AllowExponent;
                break;

            case 'F':
            case 'f':
                options = default;
                break;

            default:
                return(System.ThrowHelper.TryParseThrowFormatException(out value, out bytesConsumed));
            }

            NumberBuffer number = default;

            if (!TryParseNumber(text, ref number, out bytesConsumed, options, out bool textUsedExponentNotation))
            {
                value = default;
                return(false);
            }

            if ((!textUsedExponentNotation) && (standardFormat == 'E' || standardFormat == 'e'))
            {
                value         = default;
                bytesConsumed = 0;
                return(false);
            }

            // More compat with .NET behavior - whether or not a 0 keeps the negative sign depends on whether it an "integer" 0 or a "fractional" 0
            if (number.Digits[0] == 0 && number.Scale == 0)
            {
                number.IsNegative = false;
            }

            value = default;
            if (!Number.NumberBufferToDecimal(ref number, ref value))
            {
                value         = default;
                bytesConsumed = 0;
                return(false);
            }

            return(true);
        }
            private unsafe static Boolean HexNumberToUInt32(ref NumberBuffer number, ref UInt32 value)
            {
                Int32 i = number.scale;
                if (i > UINT32_PRECISION || i < number.precision)
                {
                    return false;
                }
                Char* p = number.digits;
                Debug.Assert(p != null, "");

                UInt32 n = 0;
                while (--i >= 0)
                {
                    if (n > ((UInt32)0xFFFFFFFF / 16))
                    {
                        return false;
                    }
                    n *= 16;
                    if (*p != '\0')
                    {
                        UInt32 newN = n;
                        if (*p != '\0')
                        {
                            if (*p >= '0' && *p <= '9')
                            {
                                newN += (UInt32)(*p - '0');
                            }
                            else
                            {
                                if (*p >= 'A' && *p <= 'F')
                                {
                                    newN += (UInt32)((*p - 'A') + 10);
                                }
                                else
                                {
                                    Debug.Assert(*p >= 'a' && *p <= 'f', "");
                                    newN += (UInt32)((*p - 'a') + 10);
                                }
                            }
                            p++;
                        }

                        // Detect an overflow here...
                        if (newN < n)
                        {
                            return false;
                        }
                        n = newN;
                    }
                }
                value = n;
                return true;
            }
Exemple #11
0
        //
        // Attempt to parse the regular floating points (the ones without names like "Infinity" and "NaN")
        //
        private static bool TryParseNormalAsFloatingPoint(ReadOnlySpan <byte> text, out double value, out int bytesConsumed, char standardFormat)
        {
            ParseNumberOptions options;

            switch (standardFormat)
            {
            case (default(char)):
            case 'G':
            case 'g':
            case 'E':
            case 'e':
                options = ParseNumberOptions.AllowExponent;
                break;

            case 'F':
            case 'f':
                options = default;
                break;

            default:
                return(System.ThrowHelper.TryParseThrowFormatException(out value, out bytesConsumed));
            }

            NumberBuffer number = default;

            if (!TryParseNumber(text, ref number, out bytesConsumed, options, out bool textUsedExponentNotation))
            {
                value = default;
                return(false);
            }

            if ((!textUsedExponentNotation) && (standardFormat == 'E' || standardFormat == 'e'))
            {
                value         = default;
                bytesConsumed = 0;
                return(false);
            }

            if (number.Digits[0] == 0)
            {
                number.IsNegative = false;
            }

            if (!Number.NumberBufferToDouble(ref number, out value))
            {
                value         = default;
                bytesConsumed = 0;
                return(false);
            }

            return(true);
        }
Exemple #12
0
        //
        // This method is copied directly from CoreRT (which is in turn a C#-ized version of the CoreCLR C++ code.)
        //
        public static void RoundNumber(ref NumberBuffer number, int pos)
        {
            number.CheckConsistency();

            Span <byte> digits = number.Digits;

            int i = 0;

            while (i < pos && digits[i] != 0)
            {
                i++;
            }

            if (i == pos && digits[i] >= (byte)'5')
            {
                while (i > 0 && digits[i - 1] == (byte)'9')
                {
                    i--;
                }

                if (i > 0)
                {
                    digits[i - 1]++;
                }
                else
                {
                    number.Scale++;
                    digits[0] = (byte)'1';
                    i         = 1;
                }
            }
            else
            {
                while (i > 0 && digits[i - 1] == (byte)'0')
                {
                    i--;
                }
            }
            if (i == 0)
            {
                number.Scale = 0;

                if (number.Kind == NumberBufferKind.Integer)
                {
                    number.IsNegative = false;
                }
            }
            digits[i] = 0;

            number.CheckConsistency();
        }
Exemple #13
0
        internal unsafe static Int32 ParseInt32(String s, NumberFormatInfo info)
        {
            Byte *       numberBufferBytes = stackalloc Byte[NumberBuffer.NumberBufferBytes];
            NumberBuffer number            = new NumberBuffer(numberBufferBytes);
            Int32        i = 0;

            StringToNumber(s, ref number, info, false);

            if (!NumberToInt32(ref number, ref i))
            {
                throw new OverflowException("SR.Overflow_Int32");
            }
            return(i);
        }
Exemple #14
0
        private static unsafe void RoundNumber(ref NumberBuffer number, int pos, bool isCorrectlyRounded)
        {
            byte *dig = number.GetDigitsPointer();

            int i = 0;

            while (i < pos && dig[i] != (byte)'\0')
            {
                i++;
            }

            if ((i == pos) && ShouldRoundUp(dig, i, isCorrectlyRounded))
            {
                while (i > 0 && dig[i - 1] == (byte)'9')
                {
                    i--;
                }

                if (i > 0)
                {
                    dig[i - 1]++;
                }
                else
                {
                    number.Scale++;
                    dig[0] = (byte)('1');
                    i      = 1;
                }
            }
            else
            {
                while (i > 0 && dig[i - 1] == (byte)'0')
                {
                    i--;
                }
            }

            if (i == 0)
            {
                number.Scale = 0;      // Decimals with scale ('0.00') should be rounded.
            }

            dig[i]             = (byte)('\0');
            number.DigitsCount = i;
        }
 private static unsafe bool HexNumberToUInt32(ref NumberBuffer number, ref uint value)
 {
     int scale = number.scale;
     if ((scale > 10) || (scale < number.precision))
     {
         return false;
     }
     char* digits = number.digits;
     uint num2 = 0;
     while (--scale >= 0)
     {
         if (num2 > 0xfffffff)
         {
             return false;
         }
         num2 *= 0x10;
         if (digits[0] != '\0')
         {
             uint num3 = num2;
             if (digits[0] != '\0')
             {
                 if ((digits[0] >= '0') && (digits[0] <= '9'))
                 {
                     num3 += digits[0] - '0';
                 }
                 else if ((digits[0] >= 'A') && (digits[0] <= 'F'))
                 {
                     num3 += (uint) ((digits[0] - 'A') + 10);
                 }
                 else
                 {
                     num3 += (uint) ((digits[0] - 'a') + 10);
                 }
                 digits++;
             }
             if (num3 < num2)
             {
                 return false;
             }
             num2 = num3;
         }
     }
     value = num2;
     return true;
 }
        //
        // Convert a Number to a double.
        //
        internal static bool NumberBufferToDouble(ref NumberBuffer number, out double value)
        {
            double d = NumberToDouble(ref number);

            if (!Double.IsFinite(d))
            {
                value = default;
                return(false);
            }

            if (d == 0.0)
            {
                // normalize -0.0 to 0.0
                d = 0.0;
            }

            value = d;
            return(true);
        }
Exemple #17
0
        private unsafe static void StringToNumber(String str, ref NumberBuffer number, NumberFormatInfo info, Boolean parseDecimal)
        {
            if (str == null)
            {
                throw new ArgumentNullException(nameof(String));
            }
            Contract.EndContractBlock();
            Debug.Assert(info != null, "");
            fixed(char *stringPointer = str)
            {
                char *p = stringPointer;

                if (!ParseNumber(ref p, ref number, null, info, parseDecimal) ||
                    (p - stringPointer < str.Length && !TrailingZeros(str, (int)(p - stringPointer))))
                {
                    throw new FormatException("SR.Format_InvalidString");
                }
            }
        }
Exemple #18
0
        private unsafe static Boolean NumberToInt32(ref NumberBuffer number, ref Int32 value)
        {
            Int32 i = number.scale;

            if (i > Int32Precision || i < number.precision)
            {
                return(false);
            }
            char *p = number.digits;

            Debug.Assert(p != null, "");
            Int32 n = 0;

            while (--i >= 0)
            {
                if ((UInt32)n > (0x7FFFFFFF / 10))
                {
                    return(false);
                }
                n *= 10;
                if (*p != '\0')
                {
                    n += (Int32)(*p++ - '0');
                }
            }
            if (number.sign)
            {
                n = -n;
                if (n > 0)
                {
                    return(false);
                }
            }
            else
            {
                if (n < 0)
                {
                    return(false);
                }
            }
            value = n;
            return(true);
        }
Exemple #19
0
        //
        // Convert a Number to a double.
        //
        internal static bool NumberBufferToDouble(ref NumberBuffer number, out double value)
        {
            double d = NumberToDouble(ref number);

            uint  e = DoubleHelper.Exponent(d);
            ulong m = DoubleHelper.Mantissa(d);

            if (e == 0x7FF)
            {
                value = default;
                return(false);
            }

            if (e == 0 && m == 0)
            {
                d = 0;
            }

            value = d;

            return(true);
        }
Exemple #20
0
        private static unsafe void ConvertIntegerToString(byte *dest, ref int destIndex, int destLength, long value, FormatOptions options)
        {
            var basis = options.GetBase();

            if (basis < 2 || basis > 36)
            {
                return;
            }

            // Calculate the full length (including zero padding)
            int length = 0;
            var tmp    = value;

            do
            {
                tmp /= basis;
                length++;
            } while (tmp != 0);

            // Write the characters for the numbers to a temp buffer
            byte *tmpBuffer = stackalloc byte[length + 1];

            tmp = value;
            int tmpIndex = length - 1;

            do
            {
                tmpBuffer[tmpIndex--] = ValueToIntegerChar((int)(tmp % basis), options.Uppercase);
                tmp /= basis;
            } while (tmp != 0);
            tmpBuffer[length] = 0;

            var numberBuffer = new NumberBuffer(NumberBufferKind.Integer, tmpBuffer, length, length, value < 0);

            FormatNumber(dest, ref destIndex, destLength, ref numberBuffer, options.Specifier, options);
        }
 public static void DecimalToNumber(decimal value, ref NumberBuffer number)
 {
     ref MutableDecimal d = ref Unsafe.As <decimal, MutableDecimal>(ref value);
 internal static unsafe bool TryParseUInt64(string s, NumberStyles style, NumberFormatInfo info, out ulong result)
 {
     byte* stackBuffer = stackalloc byte[0x72];
     NumberBuffer number = new NumberBuffer(stackBuffer);
     result = 0L;
     if (!TryStringToNumber(s, style, ref number, info, false))
     {
         return false;
     }
     if ((style & NumberStyles.AllowHexSpecifier) != NumberStyles.None)
     {
         if (!HexNumberToUInt64(ref number, ref result))
         {
             return false;
         }
     }
     else if (!NumberToUInt64(ref number, ref result))
     {
         return false;
     }
     return true;
 }
 internal static unsafe bool TryParseDouble(string value, NumberStyles options, NumberFormatInfo numfmt, out double result)
 {
     byte* stackBuffer = stackalloc byte[0x72];
     NumberBuffer number = new NumberBuffer(stackBuffer);
     result = 0.0;
     if (!TryStringToNumber(value, options, ref number, numfmt, false))
     {
         return false;
     }
     if (!NumberBufferToDouble(number.PackForNative(), ref result))
     {
         return false;
     }
     return true;
 }
 internal static unsafe ulong ParseUInt64(string value, NumberStyles options, NumberFormatInfo numfmt)
 {
     byte* stackBuffer = stackalloc byte[0x72];
     NumberBuffer number = new NumberBuffer(stackBuffer);
     ulong num = 0L;
     StringToNumber(value, options, ref number, numfmt, false);
     if ((options & NumberStyles.AllowHexSpecifier) != NumberStyles.None)
     {
         if (!HexNumberToUInt64(ref number, ref num))
         {
             throw new OverflowException(Environment.GetResourceString("Overflow_UInt64"));
         }
         return num;
     }
     if (!NumberToUInt64(ref number, ref num))
     {
         throw new OverflowException(Environment.GetResourceString("Overflow_UInt64"));
     }
     return num;
 }
        private static bool TryFormatDecimalF(ref NumberBuffer number, Span <byte> destination, out int bytesWritten, byte precision)
        {
            int scale = number.Scale;
            ReadOnlySpan <byte> digits = number.Digits;

            int numBytesNeeded =
                ((number.IsNegative) ? 1 : 0)               // minus sign
                + ((scale <= 0) ? 1 : scale)                // digits before the decimal point (minimum 1)
                + ((precision == 0) ? 0 : (precision + 1)); // if specified precision != 0, the decimal point and the digits after the decimal point (padded with zeroes if needed)

            if (destination.Length < numBytesNeeded)
            {
                bytesWritten = 0;
                return(false);
            }

            int srcIndex = 0;
            int dstIndex = 0;

            if (number.IsNegative)
            {
                destination[dstIndex++] = Utf8Constants.Minus;
            }

            //
            // Emit digits before the decimal point.
            //
            if (scale <= 0)
            {
                destination[dstIndex++] = (byte)'0';  // The integer portion is 0 and not stored. The formatter, however, needs to emit it.
            }
            else
            {
                while (srcIndex < scale)
                {
                    byte digit = digits[srcIndex];
                    if (digit == 0)
                    {
                        int numTrailingZeroes = scale - srcIndex;
                        for (int i = 0; i < numTrailingZeroes; i++)
                        {
                            destination[dstIndex++] = (byte)'0';
                        }
                        break;
                    }

                    destination[dstIndex++] = digit;
                    srcIndex++;
                }
            }

            if (precision > 0)
            {
                destination[dstIndex++] = Utf8Constants.Period;

                //
                // Emit digits after the decimal point.
                //
                int numDigitsEmitted = 0;
                if (scale < 0)
                {
                    int numLeadingZeroesToEmit = Math.Min((int)precision, -scale);
                    for (int i = 0; i < numLeadingZeroesToEmit; i++)
                    {
                        destination[dstIndex++] = (byte)'0';
                    }
                    numDigitsEmitted += numLeadingZeroesToEmit;
                }

                while (numDigitsEmitted < precision)
                {
                    byte digit = digits[srcIndex];
                    if (digit == 0)
                    {
                        while (numDigitsEmitted++ < precision)
                        {
                            destination[dstIndex++] = (byte)'0';
                        }
                        break;
                    }
                    destination[dstIndex++] = digit;
                    srcIndex++;
                    numDigitsEmitted++;
                }
            }

            Debug.Assert(dstIndex == numBytesNeeded);
            bytesWritten = numBytesNeeded;
            return(true);
        }
 private static unsafe bool ParseNumber(ref char* str, NumberStyles options, ref NumberBuffer number, StringBuilder sb, NumberFormatInfo numfmt, bool parseDecimal)
 {
     string currencyDecimalSeparator;
     string currencyGroupSeparator;
     char* chPtr2;
     number.scale = 0;
     number.sign = false;
     string currencySymbol = null;
     string ansiCurrencySymbol = null;
     string numberDecimalSeparator = null;
     string numberGroupSeparator = null;
     bool flag = false;
     if ((options & NumberStyles.AllowCurrencySymbol) != NumberStyles.None)
     {
         currencySymbol = numfmt.CurrencySymbol;
         if (numfmt.ansiCurrencySymbol != null)
         {
             ansiCurrencySymbol = numfmt.ansiCurrencySymbol;
         }
         numberDecimalSeparator = numfmt.NumberDecimalSeparator;
         numberGroupSeparator = numfmt.NumberGroupSeparator;
         currencyDecimalSeparator = numfmt.CurrencyDecimalSeparator;
         currencyGroupSeparator = numfmt.CurrencyGroupSeparator;
         flag = true;
     }
     else
     {
         currencyDecimalSeparator = numfmt.NumberDecimalSeparator;
         currencyGroupSeparator = numfmt.NumberGroupSeparator;
     }
     int num = 0;
     bool flag2 = false;
     bool flag3 = sb != null;
     bool flag4 = flag3 && ((options & NumberStyles.AllowHexSpecifier) != NumberStyles.None);
     int num2 = flag3 ? 0x7fffffff : 50;
     char* p = str;
     char ch = p[0];
     while (true)
     {
         if ((!IsWhite(ch) || ((options & NumberStyles.AllowLeadingWhite) == NumberStyles.None)) || (((num & 1) != 0) && (((num & 1) == 0) || (((num & 0x20) == 0) && (numfmt.numberNegativePattern != 2)))))
         {
             if ((flag2 = ((options & NumberStyles.AllowLeadingSign) != NumberStyles.None) && ((num & 1) == 0)) && ((chPtr2 = MatchChars(p, numfmt.positiveSign)) != null))
             {
                 num |= 1;
                 p = chPtr2 - 1;
             }
             else if (flag2 && ((chPtr2 = MatchChars(p, numfmt.negativeSign)) != null))
             {
                 num |= 1;
                 number.sign = true;
                 p = chPtr2 - 1;
             }
             else if (((ch == '(') && ((options & NumberStyles.AllowParentheses) != NumberStyles.None)) && ((num & 1) == 0))
             {
                 num |= 3;
                 number.sign = true;
             }
             else
             {
                 if (((currencySymbol == null) || ((chPtr2 = MatchChars(p, currencySymbol)) == null)) && ((ansiCurrencySymbol == null) || ((chPtr2 = MatchChars(p, ansiCurrencySymbol)) == null)))
                 {
                     break;
                 }
                 num |= 0x20;
                 currencySymbol = null;
                 ansiCurrencySymbol = null;
                 p = chPtr2 - 1;
             }
         }
         ch = *(++p);
     }
     int num3 = 0;
     int index = 0;
     while (true)
     {
         if (((ch >= '0') && (ch <= '9')) || (((options & NumberStyles.AllowHexSpecifier) != NumberStyles.None) && (((ch >= 'a') && (ch <= 'f')) || ((ch >= 'A') && (ch <= 'F')))))
         {
             num |= 4;
             if (((ch != '0') || ((num & 8) != 0)) || flag4)
             {
                 if (num3 < num2)
                 {
                     if (flag3)
                     {
                         sb.Append(ch);
                     }
                     else
                     {
                         number.digits[num3++] = ch;
                     }
                     if ((ch != '0') || parseDecimal)
                     {
                         index = num3;
                     }
                 }
                 if ((num & 0x10) == 0)
                 {
                     number.scale++;
                 }
                 num |= 8;
             }
             else if ((num & 0x10) != 0)
             {
                 number.scale--;
             }
         }
         else if ((((options & NumberStyles.AllowDecimalPoint) != NumberStyles.None) && ((num & 0x10) == 0)) && (((chPtr2 = MatchChars(p, currencyDecimalSeparator)) != null) || ((flag && ((num & 0x20) == 0)) && ((chPtr2 = MatchChars(p, numberDecimalSeparator)) != null))))
         {
             num |= 0x10;
             p = chPtr2 - 1;
         }
         else
         {
             if (((((options & NumberStyles.AllowThousands) == NumberStyles.None) || ((num & 4) == 0)) || ((num & 0x10) != 0)) || (((chPtr2 = MatchChars(p, currencyGroupSeparator)) == null) && ((!flag || ((num & 0x20) != 0)) || ((chPtr2 = MatchChars(p, numberGroupSeparator)) == null))))
             {
                 break;
             }
             p = chPtr2 - 1;
         }
         ch = *(++p);
     }
     bool flag5 = false;
     number.precision = index;
     if (flag3)
     {
         sb.Append('\0');
     }
     else
     {
         number.digits[index] = '\0';
     }
     if ((num & 4) != 0)
     {
         if (((ch == 'E') || (ch == 'e')) && ((options & NumberStyles.AllowExponent) != NumberStyles.None))
         {
             char* chPtr3 = p;
             ch = *(++p);
             chPtr2 = MatchChars(p, numfmt.positiveSign);
             if (chPtr2 != null)
             {
                 ch = *(p = chPtr2);
             }
             else
             {
                 chPtr2 = MatchChars(p, numfmt.negativeSign);
                 if (chPtr2 != null)
                 {
                     ch = *(p = chPtr2);
                     flag5 = true;
                 }
             }
             if ((ch >= '0') && (ch <= '9'))
             {
                 int num5 = 0;
                 do
                 {
                     num5 = (num5 * 10) + (ch - '0');
                     ch = *(++p);
                     if (num5 > 0x3e8)
                     {
                         num5 = 0x270f;
                         while ((ch >= '0') && (ch <= '9'))
                         {
                             ch = *(++p);
                         }
                     }
                 }
                 while ((ch >= '0') && (ch <= '9'));
                 if (flag5)
                 {
                     num5 = -num5;
                 }
                 number.scale += num5;
             }
             else
             {
                 p = chPtr3;
                 ch = p[0];
             }
         }
         while (true)
         {
             if (!IsWhite(ch) || ((options & NumberStyles.AllowTrailingWhite) == NumberStyles.None))
             {
                 if ((flag2 = ((options & NumberStyles.AllowTrailingSign) != NumberStyles.None) && ((num & 1) == 0)) && ((chPtr2 = MatchChars(p, numfmt.positiveSign)) != null))
                 {
                     num |= 1;
                     p = chPtr2 - 1;
                 }
                 else if (flag2 && ((chPtr2 = MatchChars(p, numfmt.negativeSign)) != null))
                 {
                     num |= 1;
                     number.sign = true;
                     p = chPtr2 - 1;
                 }
                 else if ((ch == ')') && ((num & 2) != 0))
                 {
                     num &= -3;
                 }
                 else
                 {
                     if (((currencySymbol == null) || ((chPtr2 = MatchChars(p, currencySymbol)) == null)) && ((ansiCurrencySymbol == null) || ((chPtr2 = MatchChars(p, ansiCurrencySymbol)) == null)))
                     {
                         break;
                     }
                     currencySymbol = null;
                     ansiCurrencySymbol = null;
                     p = chPtr2 - 1;
                 }
             }
             ch = *(++p);
         }
         if ((num & 2) == 0)
         {
             if ((num & 8) == 0)
             {
                 if (!parseDecimal)
                 {
                     number.scale = 0;
                 }
                 if ((num & 0x10) == 0)
                 {
                     number.sign = false;
                 }
             }
             str = p;
             return true;
         }
     }
     str = p;
     return false;
 }
 internal static unsafe int ParseInt32(string s, NumberStyles style, NumberFormatInfo info)
 {
     byte* stackBuffer = stackalloc byte[0x72];
     NumberBuffer number = new NumberBuffer(stackBuffer);
     int num = 0;
     StringToNumber(s, style, ref number, info, false);
     if ((style & NumberStyles.AllowHexSpecifier) != NumberStyles.None)
     {
         if (!HexNumberToInt32(ref number, ref num))
         {
             throw new OverflowException(Environment.GetResourceString("Overflow_Int32"));
         }
         return num;
     }
     if (!NumberToInt32(ref number, ref num))
     {
         throw new OverflowException(Environment.GetResourceString("Overflow_Int32"));
     }
     return num;
 }
        /// <summary>
        /// Formats a Decimal as a UTF8 string.
        /// </summary>
        /// <param name="value">Value to format</param>
        /// <param name="buffer">Buffer to write the UTF8-formatted value to</param>
        /// <param name="bytesWritten">Receives the length of the formatted text in bytes</param>
        /// <param name="format">The standard format to use</param>
        /// <returns>
        /// true for success. "bytesWritten" contains the length of the formatted text in bytes.
        /// false if buffer was too short. Iteratively increase the size of the buffer and retry until it succeeds.
        /// </returns>
        /// <remarks>
        /// Formats supported:
        ///     G/g  (default)
        ///     F/f             12.45       Fixed point
        ///     E/e             1.245000e1  Exponential
        /// </remarks>
        /// <exceptions>
        /// <cref>System.FormatException</cref> if the format is not valid for this data type.
        /// </exceptions>
        public static bool TryFormat(decimal value, Span <byte> buffer, out int bytesWritten, StandardFormat format = default)
        {
            if (format.IsDefault)
            {
                format = 'G';
            }

            switch (format.Symbol)
            {
            case 'g':
            case 'G':
            {
                if (format.Precision != StandardFormat.NoPrecision)
                {
                    throw new NotSupportedException(SR.Argument_GWithPrecisionNotSupported);
                }
                NumberBuffer number = default;
                Number.DecimalToNumber(value, ref number);
                if (number.Digits[0] == 0)
                {
                    number.IsNegative = false;         // For Decimals, -0 must print as normal 0.
                }
                bool success = TryFormatDecimalG(ref number, buffer, out bytesWritten);
#if DEBUG
                // This DEBUG segment exists to close a code coverage hole inside TryFormatDecimalG(). Because we don't call RoundNumber() on this path, we have no way to feed
                // TryFormatDecimalG() a number where trailing zeros before the decimal point have been cropped. So if the chance comes up, we'll crop the zeroes
                // ourselves and make a second call to ensure we get the same outcome.
                if (success)
                {
                    Span <byte> digits    = number.Digits;
                    int         numDigits = number.NumDigits;
                    if (numDigits != 0 && number.Scale == numDigits && digits[numDigits - 1] == '0')
                    {
                        while (numDigits != 0 && digits[numDigits - 1] == '0')
                        {
                            digits[numDigits - 1] = 0;
                            numDigits--;
                        }

                        number.CheckConsistency();

                        byte[] buffer2  = new byte[buffer.Length];
                        bool   success2 = TryFormatDecimalG(ref number, buffer2, out int bytesWritten2);
                        Debug.Assert(success2);
                        Debug.Assert(bytesWritten2 == bytesWritten);
                        for (int i = 0; i < bytesWritten; i++)
                        {
                            Debug.Assert(buffer[i] == buffer2[i]);
                        }
                    }
                }
#endif // DEBUG
                return(success);
            }

            case 'f':
            case 'F':
            {
                NumberBuffer number = default;
                Number.DecimalToNumber(value, ref number);
                byte precision = (format.Precision == StandardFormat.NoPrecision) ? (byte)2 : format.Precision;
                Number.RoundNumber(ref number, number.Scale + precision);
                Debug.Assert(!(number.Digits[0] == 0 && number.IsNegative));           // For Decimals, -0 must print as normal 0. As it happens, Number.RoundNumber already ensures this invariant.
                return(TryFormatDecimalF(ref number, buffer, out bytesWritten, precision));
            }

            case 'e':
            case 'E':
            {
                NumberBuffer number = default;
                Number.DecimalToNumber(value, ref number);
                byte precision = (format.Precision == StandardFormat.NoPrecision) ? (byte)6 : format.Precision;
                Number.RoundNumber(ref number, precision + 1);
                Debug.Assert(!(number.Digits[0] == 0 && number.IsNegative));           // For Decimals, -0 must print as normal 0. As it happens, Number.RoundNumber already ensures this invariant.
                return(TryFormatDecimalE(ref number, buffer, out bytesWritten, precision, exponentSymbol: (byte)format.Symbol));
            }

            default:
                return(ThrowHelper.TryFormatThrowFormatException(out bytesWritten));
            }
        }
        private static bool TryParseNumber(ReadOnlySpan <byte> text, ref NumberBuffer number, out int bytesConsumed, ParseNumberOptions options, out bool textUsedExponentNotation)
        {
            Debug.Assert(number.Digits[0] == 0 && number.Scale == 0 && !number.IsNegative, "Number not initialized to default(NumberBuffer)");

            textUsedExponentNotation = false;

            if (text.Length == 0)
            {
                bytesConsumed = 0;
                return(false);
            }

            Span <byte> digits = number.Digits;

            int srcIndex = 0;
            int dstIndex = 0;

            // Consume the leading sign if any.
            byte c = text[srcIndex];

            switch (c)
            {
            case Utf8Constants.Minus:
                number.IsNegative = true;
                goto case Utf8Constants.Plus;

            case Utf8Constants.Plus:
                srcIndex++;
                if (srcIndex == text.Length)
                {
                    bytesConsumed = 0;
                    return(false);
                }
                c = text[srcIndex];
                break;

            default:
                break;
            }

            int startIndexDigitsBeforeDecimal = srcIndex;

            // Throw away any leading zeroes
            while (srcIndex != text.Length)
            {
                c = text[srcIndex];
                if (c != '0')
                {
                    break;
                }
                srcIndex++;
            }

            if (srcIndex == text.Length)
            {
                digits[0]     = 0;
                number.Scale  = 0;
                bytesConsumed = srcIndex;
                number.CheckConsistency();
                return(true);
            }

            int startIndexNonLeadingDigitsBeforeDecimal = srcIndex;

            while (srcIndex != text.Length)
            {
                c = text[srcIndex];
                if ((c - 48u) > 9)
                {
                    break;
                }
                srcIndex++;
            }

            int numDigitsBeforeDecimal           = srcIndex - startIndexDigitsBeforeDecimal;
            int numNonLeadingDigitsBeforeDecimal = srcIndex - startIndexNonLeadingDigitsBeforeDecimal;

            Debug.Assert(dstIndex == 0);
            int numNonLeadingDigitsBeforeDecimalToCopy = Math.Min(numNonLeadingDigitsBeforeDecimal, NumberBuffer.BufferSize - 1);

            text.Slice(startIndexNonLeadingDigitsBeforeDecimal, numNonLeadingDigitsBeforeDecimalToCopy).CopyTo(digits);
            dstIndex     = numNonLeadingDigitsBeforeDecimalToCopy;
            number.Scale = numNonLeadingDigitsBeforeDecimal;

            if (srcIndex == text.Length)
            {
                bytesConsumed = srcIndex;
                number.CheckConsistency();
                return(true);
            }

            int numDigitsAfterDecimal = 0;

            if (c == Utf8Constants.Period)
            {
                //
                // Parse the digits after the decimal point.
                //

                srcIndex++;
                int startIndexDigitsAfterDecimal = srcIndex;
                while (srcIndex != text.Length)
                {
                    c = text[srcIndex];
                    if ((c - 48u) > 9)
                    {
                        break;
                    }
                    srcIndex++;
                }
                numDigitsAfterDecimal = srcIndex - startIndexDigitsAfterDecimal;

                int startIndexOfDigitsAfterDecimalToCopy = startIndexDigitsAfterDecimal;
                if (dstIndex == 0)
                {
                    // Not copied any digits to the Number struct yet. This means we must continue discarding leading zeroes even though they appeared after the decimal point.
                    while (startIndexOfDigitsAfterDecimalToCopy < srcIndex && text[startIndexOfDigitsAfterDecimalToCopy] == '0')
                    {
                        number.Scale--;
                        startIndexOfDigitsAfterDecimalToCopy++;
                    }
                }

                int numDigitsAfterDecimalToCopy = Math.Min(srcIndex - startIndexOfDigitsAfterDecimalToCopy, NumberBuffer.BufferSize - dstIndex - 1);
                text.Slice(startIndexOfDigitsAfterDecimalToCopy, numDigitsAfterDecimalToCopy).CopyTo(digits.Slice(dstIndex));
                dstIndex += numDigitsAfterDecimalToCopy;
                // We "should" really NUL terminate, but there are multiple places we'd have to do this and it is a precondition that the caller pass in a fully zero=initialized Number.

                if (srcIndex == text.Length)
                {
                    if (numDigitsBeforeDecimal == 0 && numDigitsAfterDecimal == 0)
                    {
                        // For compatibility. You can say "5." and ".5" but you can't say "."
                        bytesConsumed = 0;
                        return(false);
                    }

                    bytesConsumed = srcIndex;
                    number.CheckConsistency();
                    return(true);
                }
            }

            if (numDigitsBeforeDecimal == 0 && numDigitsAfterDecimal == 0)
            {
                bytesConsumed = 0;
                return(false);
            }

            if ((c & ~0x20u) != 'E')
            {
                bytesConsumed = srcIndex;
                number.CheckConsistency();
                return(true);
            }

            //
            // Parse the exponent after the "E"
            //
            textUsedExponentNotation = true;
            srcIndex++;

            if ((options & ParseNumberOptions.AllowExponent) == 0)
            {
                bytesConsumed = 0;
                return(false);
            }

            if (srcIndex == text.Length)
            {
                bytesConsumed = 0;
                return(false);
            }

            bool exponentIsNegative = false;

            c = text[srcIndex];
            switch (c)
            {
            case Utf8Constants.Minus:
                exponentIsNegative = true;
                goto case Utf8Constants.Plus;

            case Utf8Constants.Plus:
                srcIndex++;
                if (srcIndex == text.Length)
                {
                    bytesConsumed = 0;
                    return(false);
                }
                c = text[srcIndex];
                break;

            default:
                break;
            }

            if (!Utf8Parser.TryParseUInt32D(text.Slice(srcIndex), out uint absoluteExponent, out int bytesConsumedByExponent))
            {
                bytesConsumed = 0;
                return(false);
            }

            srcIndex += bytesConsumedByExponent;

            if (exponentIsNegative)
            {
                if (number.Scale < int.MinValue + (long)absoluteExponent)
                {
                    // A scale underflow means all non-zero digits are all so far to the right of the decimal point, no
                    // number format we have will be able to see them. Just pin the scale at the absolute minimum
                    // and let the converter produce a 0 with the max precision available for that type.
                    number.Scale = int.MinValue;
                }
                else
                {
                    number.Scale -= (int)absoluteExponent;
                }
            }
            else
            {
                if (number.Scale > int.MaxValue - (long)absoluteExponent)
                {
                    bytesConsumed = 0;
                    return(false);
                }
                number.Scale += (int)absoluteExponent;
            }

            bytesConsumed = srcIndex;
            number.CheckConsistency();
            return(true);
        }
Exemple #30
0
        private unsafe static Boolean ParseNumber(ref char *str, ref NumberBuffer number, StringBuilder sb, NumberFormatInfo numfmt, Boolean parseDecimal)
        {
            const Int32 StateSign     = 0x0001;
            const Int32 StateParens   = 0x0002;
            const Int32 StateDigits   = 0x0004;
            const Int32 StateNonZero  = 0x0008;
            const Int32 StateDecimal  = 0x0010;
            const Int32 StateCurrency = 0x0020;

            number.scale = 0;
            number.sign  = false;
            string currSymbol = null;       // currency symbol from NumberFormatInfo.

            Int32   state          = 0;
            Boolean bigNumber      = (sb != null); // When a StringBuilder is provided then we use it in place of the number.digits char[50]
            Int32   maxParseDigits = bigNumber ? Int32.MaxValue : NumberMaxDigits;

            char *p  = str;
            char  ch = *p;
            char *next;

            while (true)
            {
                // Eat whitespace unless we've found a sign which isn't followed by a currency symbol.
                // "-Kr 1231.47" is legal but "- 1231.47" is not.
                if (!IsWhite(ch) || ((state & StateSign) != 0 && ((state & StateCurrency) == 0 && numfmt.NumberNegativePattern != 2)))
                {
                    if (((state & StateSign) == 0) && ((next = MatchChars(p, numfmt.PositiveSign)) != null || ((next = MatchChars(p, numfmt.NegativeSign)) != null && (number.sign = true))))
                    {
                        state |= StateSign;
                        p      = next - 1;
                    }
                    else if (currSymbol != null && (next = MatchChars(p, currSymbol)) != null)
                    {
                        state     |= StateCurrency;
                        currSymbol = null;
                        // We already found the currency symbol. There should not be more currency symbols. Set
                        // currSymbol to NULL so that we won't search it again in the later code path.
                        p = next - 1;
                    }
                    else
                    {
                        break;
                    }
                }
                ch = *++p;
            }
            Int32 digCount = 0;
            Int32 digEnd   = 0;

            while (true)
            {
                if ((ch >= '0' && ch <= '9'))
                {
                    state |= StateDigits;

                    if (ch != '0' || (state & StateNonZero) != 0)
                    {
                        if (digCount < maxParseDigits)
                        {
                            if (bigNumber)
                            {
                                sb.Append(ch);
                            }
                            else
                            {
                                number.digits[digCount++] = ch;
                            }
                            if (ch != '0' || parseDecimal)
                            {
                                digEnd = digCount;
                            }
                        }
                        if ((state & StateDecimal) == 0)
                        {
                            number.scale++;
                        }
                        state |= StateNonZero;
                    }
                    else if ((state & StateDecimal) != 0)
                    {
                        number.scale--;
                    }
                }
                else
                {
                    break;
                }
                ch = *++p;
            }

            number.precision = digEnd;
            if (bigNumber)
            {
                sb.Append('\0');
            }
            else
            {
                number.digits[digEnd] = '\0';
            }
            if ((state & StateDigits) != 0)
            {
                while (true)
                {
                    if (!IsWhite(ch))
                    {
                        if ((((state & StateSign) == 0)) && ((next = MatchChars(p, numfmt.PositiveSign)) != null || (((next = MatchChars(p, numfmt.NegativeSign)) != null) && (number.sign = true))))
                        {
                            state |= StateSign;
                            p      = next - 1;
                        }
                        else if (ch == ')' && ((state & StateParens) != 0))
                        {
                            state &= ~StateParens;
                        }
                        else if (currSymbol != null && (next = MatchChars(p, currSymbol)) != null)
                        {
                            currSymbol = null;
                            p          = next - 1;
                        }
                        else
                        {
                            break;
                        }
                    }
                    ch = *++p;
                }
                if ((state & StateParens) == 0)
                {
                    if ((state & StateNonZero) == 0)
                    {
                        if (!parseDecimal)
                        {
                            number.scale = 0;
                        }
                        if ((state & StateDecimal) == 0)
                        {
                            number.sign = false;
                        }
                    }
                    str = p;
                    return(true);
                }
            }
            str = p;
            return(false);
        }
            private static unsafe void DoubleToNumber(double value, int precision, ref NumberBuffer number)
            {
                Debug.Assert(precision > 0 && precision < 40);

                number.precision = precision;
                if (DoubleHelper.Exponent(value) == 0x7ff)
                {
                    number.scale     = DoubleHelper.Mantissa(value) != 0 ? SCALE_NAN : SCALE_INF;
                    number.sign      = DoubleHelper.Sign(value);
                    number.digits[0] = '\0';
                    return;
                }

                byte *tempBuffer = stackalloc byte[MAX_BUFFER_SIZE];
                char *dst        = number.digits;

                number.scale = 0;
                number.sign  = false;
                *dst = '\0';

                if (value < 0.0)
                {
                    number.sign = true;
                }

                if (value == 0.0)
                {
                    for (int j = 0; j < precision; j++)
                    {
                        dst[j] = '0';
                    }
                    dst[precision] = '\0';
                    return;
                }

                //
                // Get the number formatted as a string in the form x.xxxxxxexxxx
                //

                // "%.40e"
                byte *format = stackalloc byte[6];

                format[0] = (byte)'%';
                format[1] = (byte)'.';
                format[2] = (byte)'4';
                format[3] = (byte)'0';
                format[4] = (byte)'e';
                format[5] = 0;

                int tempBufferLength = Interop.Sys.DoubleToString(value, format, tempBuffer, MAX_BUFFER_SIZE);

                Debug.Assert(tempBufferLength > 0 && MAX_BUFFER_SIZE > tempBufferLength);

                //
                // Calculate the exponent value
                //

                int exponentIndex = tempBufferLength - 1;

                while (tempBuffer[exponentIndex] != (byte)'e' && exponentIndex > 0)
                {
                    exponentIndex--;
                }

                Debug.Assert(exponentIndex > 0 && (exponentIndex < tempBufferLength - 1));

                int i            = exponentIndex + 1;
                int exponentSign = 1;

                if (tempBuffer[i] == '-')
                {
                    exponentSign = -1;
                    i++;
                }
                else if (tempBuffer[i] == '+')
                {
                    i++;
                }

                int exponentValue = 0;

                while (i < tempBufferLength)
                {
                    Debug.Assert(tempBuffer[i] >= (byte)'0' && tempBuffer[i] <= (byte)'9');
                    exponentValue = exponentValue * 10 + (tempBuffer[i] - (byte)'0');
                    i++;
                }
                exponentValue *= exponentSign;

                //
                // Determine decimal location.
                //

                if (exponentValue == 0)
                {
                    number.scale = 1;
                }
                else
                {
                    number.scale = exponentValue + 1;
                }

                //
                // Copy the string from the temp buffer upto precision characters, removing the sign, and decimal as required.
                //

                i = 0;
                int mantissaIndex = 0;

                while (i < precision && mantissaIndex < exponentIndex)
                {
                    if (tempBuffer[mantissaIndex] >= (byte)'0' && tempBuffer[mantissaIndex] <= (byte)'9')
                    {
                        dst[i] = (char)tempBuffer[mantissaIndex];
                        i++;
                    }
                    mantissaIndex++;
                }

                while (i < precision)
                {
                    dst[i] = '0'; // append zeros as needed
                    i++;
                }

                dst[i] = '\0';

                //
                // Round if needed
                //

                if (mantissaIndex >= exponentIndex || tempBuffer[mantissaIndex] < (byte)'5')
                {
                    return; // rounding is not needed
                }

                i = precision - 1;
                while (dst[i] == '9' && i > 0)
                {
                    dst[i] = '0';
                    i--;
                }

                if (i == 0 && dst[i] == '9')
                {
                    dst[i] = '1';
                    number.scale++;
                }
                else
                {
                    dst[i]++;
                }
            }
        private static unsafe void Dragon4(double value, int precision, ref NumberBuffer number)
        {
            const double Log10V2 = 0.30102999566398119521373889472449;

            // DriftFactor = 1 - Log10V2 - epsilon (a small number account for drift of floating point multiplication)
            const double DriftFactor = 0.69;

            // ========================================================================================================================================
            // This implementation is based on the paper: https://www.cs.indiana.edu/~dyb/pubs/FP-Printing-PLDI96.pdf
            // Besides the paper, some of the code and ideas are modified from http://www.ryanjuckett.com/programming/printing-floating-point-numbers/
            // You must read these two materials to fully understand the code.
            //
            // Note: we only support fixed format input.
            // ========================================================================================================================================
            //
            // Overview:
            //
            // The input double number can be represented as:
            // value = f * 2^e = r / s.
            //
            // f: the output mantissa. Note: f is not the 52 bits mantissa of the input double number.
            // e: biased exponent.
            // r: numerator.
            // s: denominator.
            // k: value = d0.d1d2 . . . dn * 10^k

            // Step 1:
            // Extract meta data from the input double value.
            //
            // Refer to IEEE double precision floating point format.
            ulong f = (ulong)(ExtractFractionAndBiasedExponent(value, out int e));
            int   mantissaHighBitIndex = (e == -1074) ? (int)(BigInteger.LogBase2(f)) : 52;

            // Step 2:
            // Estimate k. We'll verify it and fix any error later.
            //
            // This is an improvement of the estimation in the original paper.
            // Inspired by http://www.ryanjuckett.com/programming/printing-floating-point-numbers/
            //
            // LOG10V2 = 0.30102999566398119521373889472449
            // DRIFT_FACTOR = 0.69 = 1 - log10V2 - epsilon (a small number account for drift of floating point multiplication)
            int k = (int)(Math.Ceiling(((mantissaHighBitIndex + e) * Log10V2) - DriftFactor));

            // Step 3:
            // Store the input double value in BigInteger format.
            //
            // To keep the precision, we represent the double value as r/s.
            // We have several optimization based on following table in the paper.
            //
            //     ----------------------------------------------------------------------------------------------------------
            //     |               e >= 0                   |                         e < 0                                 |
            //     ----------------------------------------------------------------------------------------------------------
            //     |  f != b^(P - 1)  |  f = b^(P - 1)      | e = min exp or f != b^(P - 1) | e > min exp and f = b^(P - 1) |
            // --------------------------------------------------------------------------------------------------------------
            // | r |  f * b^e * 2     |  f * b^(e + 1) * 2  |          f * 2                |            f * b * 2          |
            // --------------------------------------------------------------------------------------------------------------
            // | s |        2         |        b * 2        |          b^(-e) * 2           |            b^(-e + 1) * 2     |
            // --------------------------------------------------------------------------------------------------------------
            //
            // Note, we do not need m+ and m- because we only support fixed format input here.
            // m+ and m- are used for free format input, which need to determine the exact range of values
            // that would round to value when input so that we can generate the shortest correct number.digits.
            //
            // In our case, we just output number.digits until reaching the expected precision.

            var r = new BigInteger(f);
            var s = new BigInteger(0);

            if (e >= 0)
            {
                // When f != b^(P - 1):
                // r = f * b^e * 2
                // s = 2
                // value = r / s = f * b^e * 2 / 2 = f * b^e / 1
                //
                // When f = b^(P - 1):
                // r = f * b^(e + 1) * 2
                // s = b * 2
                // value = r / s =  f * b^(e + 1) * 2 / b * 2 = f * b^e / 1
                //
                // Therefore, we can simply say that when e >= 0:
                // r = f * b^e = f * 2^e
                // s = 1

                r.ShiftLeft((uint)(e));
                s.SetUInt64(1);
            }
            else
            {
                // When e = min exp or f != b^(P - 1):
                // r = f * 2
                // s = b^(-e) * 2
                // value = r / s = f * 2 / b^(-e) * 2 = f / b^(-e)
                //
                // When e > min exp and f = b^(P - 1):
                // r = f * b * 2
                // s = b^(-e + 1) * 2
                // value = r / s =  f * b * 2 / b^(-e + 1) * 2 = f / b^(-e)
                //
                // Therefore, we can simply say that when e < 0:
                // r = f
                // s = b^(-e) = 2^(-e)

                BigInteger.ShiftLeft(1, (uint)(-e), ref s);
            }

            // According to the paper, we should use k >= 0 instead of k > 0 here.
            // However, if k = 0, both r and s won't be changed, we don't need to do any operation.
            //
            // Following are the Scheme code from the paper:
            // --------------------------------------------------------------------------------
            // (if (>= est 0)
            // (fixup r (* s (exptt B est)) m+ m− est B low-ok? high-ok? )
            // (let ([scale (exptt B (− est))])
            // (fixup (* r scale) s (* m+ scale) (* m− scale) est B low-ok? high-ok? ))))
            // --------------------------------------------------------------------------------
            //
            // If est is 0, (* s (exptt B est)) = s, (* r scale) = (* r (exptt B (− est)))) = r.
            //
            // So we just skip when k = 0.

            if (k > 0)
            {
                s.MultiplyPow10((uint)(k));
            }
            else if (k < 0)
            {
                r.MultiplyPow10((uint)(-k));
            }

            if (BigInteger.Compare(ref r, ref s) >= 0)
            {
                // The estimation was incorrect. Fix the error by increasing 1.
                k += 1;
            }
            else
            {
                r.Multiply10();
            }

            number.Scale = (k - 1);

            // This the prerequisite of calling BigInteger.HeuristicDivide().
            BigInteger.PrepareHeuristicDivide(ref r, ref s);

            // Step 4:
            // Calculate number.digits.
            //
            // Output number.digits until reaching the last but one precision or the numerator becomes zero.

            int digitsNum    = 0;
            int currentDigit = 0;

            while (true)
            {
                currentDigit = (int)(BigInteger.HeuristicDivide(ref r, ref s));

                if (r.IsZero() || ((digitsNum + 1) == precision))
                {
                    break;
                }

                number.Digits[digitsNum] = (char)('0' + currentDigit);
                digitsNum++;

                r.Multiply10();
            }

            // Step 5:
            // Set the last digit.
            //
            // We round to the closest digit by comparing value with 0.5:
            //  compare( value, 0.5 )
            //  = compare( r / s, 0.5 )
            //  = compare( r, 0.5 * s)
            //  = compare(2 * r, s)
            //  = compare(r << 1, s)

            r.ShiftLeft(1);
            int  compareResult = BigInteger.Compare(ref r, ref s);
            bool isRoundDown   = compareResult < 0;

            // We are in the middle, round towards the even digit (i.e. IEEE rouding rules)
            if (compareResult == 0)
            {
                isRoundDown = (currentDigit & 1) == 0;
            }

            if (isRoundDown)
            {
                number.Digits[digitsNum] = (char)('0' + currentDigit);
                digitsNum++;
            }
            else
            {
                char *pCurrentDigit = (number.GetDigitsPointer() + digitsNum);

                // Rounding up for 9 is special.
                if (currentDigit == 9)
                {
                    // find the first non-nine prior digit
                    while (true)
                    {
                        // If we are at the first digit
                        if (pCurrentDigit == number.GetDigitsPointer())
                        {
                            // Output 1 at the next highest exponent
                            *pCurrentDigit = '1';
                            digitsNum++;
                            number.Scale += 1;
                            break;
                        }

                        pCurrentDigit--;
                        digitsNum--;

                        if (*pCurrentDigit != '9')
                        {
                            // increment the digit
                            *pCurrentDigit += (char)(1);
                            digitsNum++;
                            break;
                        }
                    }
                }
                else
                {
                    // It's simple if the digit is not 9.
                    *pCurrentDigit = (char)('0' + currentDigit + 1);
                    digitsNum++;
                }
            }

            while (digitsNum < precision)
            {
                number.Digits[digitsNum] = '0';
                digitsNum++;
            }

            number.Digits[precision] = '\0';

            number.Scale++;
            number.Sign = double.IsNegative(value);
        }
Exemple #33
0
        private static unsafe int GetLengthForFormatGeneral(ref NumberBuffer number, int nMaxDigits)
        {
            // NOTE: Must be kept in sync with FormatGeneral!
            int  length     = 0;
            int  scale      = number.Scale;
            int  digPos     = scale;
            bool scientific = false;

            // Don't switch to scientific notation
            if (digPos > nMaxDigits || digPos < -3)
            {
                digPos     = 1;
                scientific = true;
            }

            byte *dig = number.GetDigitsPointer();

            if (number.IsNegative)
            {
                length++; // (byte)'-';
            }

            if (digPos > 0)
            {
                do
                {
                    if (*dig != 0)
                    {
                        dig++;
                    }
                    length++;
                } while (--digPos > 0);
            }
            else
            {
                length++;
            }

            if (*dig != 0 || digPos < 0)
            {
                length++; // (byte)'.';

                while (digPos < 0)
                {
                    length++; // (byte)'0';
                    digPos++;
                }

                while (*dig != 0)
                {
                    length++; // *dig++;
                    dig++;
                }
            }

            if (scientific)
            {
                length++; // e or E
                int exponent = number.Scale - 1;
                if (exponent >= 0)
                {
                    length++;
                }
                length += GetLengthIntegerToString(exponent, 10, 2);
            }

            return(length);
        }
Exemple #34
0
        private static unsafe void FormatGeneral(byte *dest, ref int destIndex, int destLength, ref NumberBuffer number, int nMaxDigits, byte expChar)
        {
            int  scale      = number.Scale;
            int  digPos     = scale;
            bool scientific = false;

            // Don't switch to scientific notation
            if (digPos > nMaxDigits || digPos < -3)
            {
                digPos     = 1;
                scientific = true;
            }

            byte *dig = number.GetDigitsPointer();

            if (number.IsNegative)
            {
                if (destIndex >= destLength)
                {
                    return;
                }
                dest[destIndex++] = (byte)'-';
            }

            if (digPos > 0)
            {
                do
                {
                    if (destIndex >= destLength)
                    {
                        return;
                    }
                    dest[destIndex++] = (*dig != 0) ? (byte)(*dig++) : (byte)'0';
                } while (--digPos > 0);
            }
            else
            {
                if (destIndex >= destLength)
                {
                    return;
                }
                dest[destIndex++] = (byte)'0';
            }

            if (*dig != 0 || digPos < 0)
            {
                if (destIndex >= destLength)
                {
                    return;
                }
                dest[destIndex++] = (byte)'.';

                while (digPos < 0)
                {
                    if (destIndex >= destLength)
                    {
                        return;
                    }
                    dest[destIndex++] = (byte)'0';
                    digPos++;
                }

                while (*dig != 0)
                {
                    if (destIndex >= destLength)
                    {
                        return;
                    }
                    dest[destIndex++] = *dig++;
                }
            }

            if (scientific)
            {
                if (destIndex >= destLength)
                {
                    return;
                }
                dest[destIndex++] = expChar;

                int exponent = number.Scale - 1;
                var exponentFormatOptions = new FormatOptions(NumberFormatKind.DecimalForceSigned, 0, 2, false);

                ConvertIntegerToString(dest, ref destIndex, destLength, exponent, exponentFormatOptions);
            }
        }
 private static unsafe bool NumberToUInt64(ref NumberBuffer number, ref ulong value)
 {
     int scale = number.scale;
     if (((scale > 20) || (scale < number.precision)) || number.sign)
     {
         return false;
     }
     char* digits = number.digits;
     ulong num2 = 0L;
     while (--scale >= 0)
     {
         if (num2 > 0x1999999999999999L)
         {
             return false;
         }
         num2 *= (ulong) 10L;
         if (digits[0] != '\0')
         {
             digits++;
             ulong num3 = num2 + (digits[0] - '0');
             if (num3 < num2)
             {
                 return false;
             }
             num2 = num3;
         }
     }
     value = num2;
     return true;
 }
 internal static unsafe decimal ParseDecimal(string value, NumberStyles options, NumberFormatInfo numfmt)
 {
     byte* stackBuffer = stackalloc byte[0x72];
     NumberBuffer number = new NumberBuffer(stackBuffer);
     decimal num = 0M;
     StringToNumber(value, options, ref number, numfmt, true);
     if (!NumberBufferToDecimal(number.PackForNative(), ref num))
     {
         throw new OverflowException(Environment.GetResourceString("Overflow_Decimal"));
     }
     return num;
 }
Exemple #37
0
        private static unsafe void FormatDecimalOrHexadecimal(byte *dest, ref int destIndex, int destLength, ref NumberBuffer number, int zeroPadding, bool outputPositiveSign)
        {
            if (number.IsNegative)
            {
                if (destIndex >= destLength)
                {
                    return;
                }
                dest[destIndex++] = (byte)'-';
            }
            else if (outputPositiveSign)
            {
                if (destIndex >= destLength)
                {
                    return;
                }
                dest[destIndex++] = (byte)'+';
            }

            // Zero Padding
            for (int i = 0; i < zeroPadding; i++)
            {
                if (destIndex >= destLength)
                {
                    return;
                }
                dest[destIndex++] = (byte)'0';
            }

            var   digitCount = number.DigitsCount;
            byte *digits     = number.GetDigitsPointer();

            for (int i = 0; i < digitCount; i++)
            {
                if (destIndex >= destLength)
                {
                    return;
                }
                dest[destIndex++] = digits[i];
            }
        }
Exemple #38
0
        private static unsafe void FormatNumber(byte *dest, ref int destIndex, int destLength, ref NumberBuffer number, int nMaxDigits, FormatOptions options)
        {
            bool isCorrectlyRounded = (number.Kind == NumberBufferKind.Float);

            // If we have an integer, and the rendering is the default `G`, then use Decimal rendering which is faster
            if (number.Kind == NumberBufferKind.Integer && options.Kind == NumberFormatKind.General && options.Specifier == 0)
            {
                options.Kind = NumberFormatKind.Decimal;
            }

            int length;

            switch (options.Kind)
            {
            case NumberFormatKind.DecimalForceSigned:
            case NumberFormatKind.Decimal:
            case NumberFormatKind.Hexadecimal:
                length = number.DigitsCount;

                var zeroPadding       = (int)options.Specifier;
                int actualZeroPadding = 0;
                if (length < zeroPadding)
                {
                    actualZeroPadding = zeroPadding - length;
                    length            = zeroPadding;
                }

                bool outputPositiveSign = options.Kind == NumberFormatKind.DecimalForceSigned;
                length += number.IsNegative || outputPositiveSign ? 1 : 0;

                // Perform left align
                if (AlignLeft(dest, ref destIndex, destLength, options.AlignAndSize, length))
                {
                    return;
                }

                FormatDecimalOrHexadecimal(dest, ref destIndex, destLength, ref number, actualZeroPadding, outputPositiveSign);

                // Perform right align
                AlignRight(dest, ref destIndex, destLength, options.AlignAndSize, length);

                break;

            default:
            case NumberFormatKind.General:

                if (nMaxDigits < 1)
                {
                    // This ensures that the PAL code pads out to the correct place even when we use the default precision
                    nMaxDigits = number.DigitsCount;
                }

                RoundNumber(ref number, nMaxDigits, isCorrectlyRounded);

                // Calculate final rendering length
                length = GetLengthForFormatGeneral(ref number, nMaxDigits);

                // Perform left align
                if (AlignLeft(dest, ref destIndex, destLength, options.AlignAndSize, length))
                {
                    return;
                }

                // Format using general formatting
                FormatGeneral(dest, ref destIndex, destLength, ref number, nMaxDigits, options.Uppercase ? (byte)'E' : (byte)'e');

                // Perform right align
                AlignRight(dest, ref destIndex, destLength, options.AlignAndSize, length);
                break;
            }
        }
 internal static unsafe bool TryStringToNumber(string str, NumberStyles options, ref NumberBuffer number, StringBuilder sb, NumberFormatInfo numfmt, bool parseDecimal)
 {
     if (str == null)
     {
         return false;
     }
     fixed (char* str2 = ((char*) str))
     {
         char* chPtr = str2;
         char* chPtr2 = chPtr;
         if (!ParseNumber(ref chPtr2, options, ref number, sb, numfmt, parseDecimal) || ((((long) ((chPtr2 - chPtr) / 2)) < str.Length) && !TrailingZeros(str, (int) ((long) ((chPtr2 - chPtr) / 2)))))
         {
             return false;
         }
     }
     return true;
 }
            private static unsafe void RoundNumber(ref NumberBuffer number, int pos)
            {
                char* dig = number.digits;

                int i = 0;
                while (i < pos && dig[i] != 0)
                    i++;

                if (i == pos && dig[i] >= '5')
                {
                    while (i > 0 && dig[i - 1] == '9')
                        i--;

                    if (i > 0)
                    {
                        dig[i - 1]++;
                    }
                    else
                    {
                        number.scale++;
                        dig[0] = '1';
                        i = 1;
                    }
                }
                else
                {
                    while (i > 0 && dig[i - 1] == '0')
                        i--;
                }
                if (i == 0)
                {
                    number.scale = 0;
                    number.sign = false;
                }
                dig[i] = '\0';
            }
            private static unsafe bool ParseNumber(ref char* str, NumberStyles options, ref NumberBuffer number, StringBuilder sb, NumberFormatInfo numfmt, bool parseDecimal)
            {
                const int StateSign = 0x0001;
                const int StateParens = 0x0002;
                const int StateDigits = 0x0004;
                const int StateNonZero = 0x0008;
                const int StateDecimal = 0x0010;
                const int StateCurrency = 0x0020;

                number.scale = 0;
                number.sign = false;
                string decSep;                  // Decimal separator from NumberFormatInfo.
                string groupSep;                // Group separator from NumberFormatInfo.
                string currSymbol = null;       // Currency symbol from NumberFormatInfo.

                bool parsingCurrency = false;
                if ((options & NumberStyles.AllowCurrencySymbol) != 0)
                {
                    currSymbol = numfmt.CurrencySymbol;
                    // The idea here is to match the currency separators and on failure match the number separators to keep the perf of VB's IsNumeric fast.
                    // The values of decSep are setup to use the correct relevant separator (currency in the if part and decimal in the else part).
                    decSep = numfmt.CurrencyDecimalSeparator;
                    groupSep = numfmt.CurrencyGroupSeparator;
                    parsingCurrency = true;
                }
                else
                {
                    decSep = numfmt.NumberDecimalSeparator;
                    groupSep = numfmt.NumberGroupSeparator;
                }

                int state = 0;
                bool bigNumber = (sb != null); // When a StringBuilder is provided then we use it in place of the number.digits char[50]
                int maxParseDigits = bigNumber ? int.MaxValue : NumberMaxDigits;

                char* p = str;
                char ch = *p;
                char* next;

                char* dig = number.digits;

                while (true)
                {
                    // Eat whitespace unless we've found a sign which isn't followed by a currency symbol.
                    // "-Kr 1231.47" is legal but "- 1231.47" is not.
                    if (!IsWhite(ch) || (options & NumberStyles.AllowLeadingWhite) == 0 || ((state & StateSign) != 0 && ((state & StateCurrency) == 0 && numfmt.NumberNegativePattern != 2)))
                    {
                        if ((((options & NumberStyles.AllowLeadingSign) != 0) && (state & StateSign) == 0) && ((next = MatchChars(p, numfmt.PositiveSign)) != null || ((next = MatchChars(p, numfmt.NegativeSign)) != null && (number.sign = true))))
                        {
                            state |= StateSign;
                            p = next - 1;
                        }
                        else if (ch == '(' && ((options & NumberStyles.AllowParentheses) != 0) && ((state & StateSign) == 0))
                        {
                            state |= StateSign | StateParens;
                            number.sign = true;
                        }
                        else if (currSymbol != null && (next = MatchChars(p, currSymbol)) != null)
                        {
                            state |= StateCurrency;
                            currSymbol = null;  

                            // We already found the currency symbol. There should not be more currency symbols. Set
                            // currSymbol to NULL so that we won't search it again in the later code path.
                            p = next - 1;
                        }
                        else
                        {
                            break;
                        }
                    }
                    ch = *++p;
                }

                int digCount = 0;
                int digEnd = 0;
                while (true)
                {
                    if ((ch >= '0' && ch <= '9') || (((options & NumberStyles.AllowHexSpecifier) != 0) && ((ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F'))))
                    {
                        state |= StateDigits;

                        if (ch != '0' || (state & StateNonZero) != 0 || (bigNumber && ((options & NumberStyles.AllowHexSpecifier) != 0)))
                        {
                            if (digCount < maxParseDigits)
                            {
                                if (bigNumber)
                                    sb.Append(ch);
                                else
                                    dig[digCount++] = ch;
                                if (ch != '0' || parseDecimal)
                                {
                                    digEnd = digCount;
                                }
                            }
                            if ((state & StateDecimal) == 0)
                            {
                                number.scale++;
                            }
                            state |= StateNonZero;
                        }
                        else if ((state & StateDecimal) != 0)
                        {
                            number.scale--;
                        }
                    }
                    else if (((options & NumberStyles.AllowDecimalPoint) != 0) && ((state & StateDecimal) == 0) && ((next = MatchChars(p, decSep)) != null || ((parsingCurrency) && (state & StateCurrency) == 0) && (next = MatchChars(p, numfmt.NumberDecimalSeparator)) != null))
                    {
                        state |= StateDecimal;
                        p = next - 1;
                    }
                    else if (((options & NumberStyles.AllowThousands) != 0) && ((state & StateDigits) != 0) && ((state & StateDecimal) == 0) && ((next = MatchChars(p, groupSep)) != null || ((parsingCurrency) && (state & StateCurrency) == 0) && (next = MatchChars(p, numfmt.NumberGroupSeparator)) != null))
                    {
                        p = next - 1;
                    }
                    else
                    {
                        break;
                    }
                    ch = *++p;
                }

                bool negExp = false;
                number.precision = digEnd;
                if (bigNumber)
                    sb.Append('\0');
                else
                    dig[digEnd] = '\0';
                if ((state & StateDigits) != 0)
                {
                    if ((ch == 'E' || ch == 'e') && ((options & NumberStyles.AllowExponent) != 0))
                    {
                        char* temp = p;
                        ch = *++p;
                        if ((next = MatchChars(p, numfmt.PositiveSign)) != null)
                        {
                            ch = *(p = next);
                        }
                        else if ((next = MatchChars(p, numfmt.NegativeSign)) != null)
                        {
                            ch = *(p = next);
                            negExp = true;
                        }
                        if (ch >= '0' && ch <= '9')
                        {
                            int exp = 0;
                            do
                            {
                                exp = exp * 10 + (ch - '0');
                                ch = *++p;
                                if (exp > 1000)
                                {
                                    exp = 9999;
                                    while (ch >= '0' && ch <= '9')
                                    {
                                        ch = *++p;
                                    }
                                }
                            } while (ch >= '0' && ch <= '9');
                            if (negExp)
                            {
                                exp = -exp;
                            }
                            number.scale += exp;
                        }
                        else
                        {
                            p = temp;
                            ch = *p;
                        }
                    }
                    while (true)
                    {
                        if (!IsWhite(ch) || (options & NumberStyles.AllowTrailingWhite) == 0)
                        {
                            if (((options & NumberStyles.AllowTrailingSign) != 0 && ((state & StateSign) == 0)) && ((next = MatchChars(p, numfmt.PositiveSign)) != null || (((next = MatchChars(p, numfmt.NegativeSign)) != null) && (number.sign = true))))
                            {
                                state |= StateSign;
                                p = next - 1;
                            }
                            else if (ch == ')' && ((state & StateParens) != 0))
                            {
                                state &= ~StateParens;
                            }
                            else if (currSymbol != null && (next = MatchChars(p, currSymbol)) != null)
                            {
                                currSymbol = null;
                                p = next - 1;
                            }
                            else
                            {
                                break;
                            }
                        }
                        ch = *++p;
                    }
                    if ((state & StateParens) == 0)
                    {
                        if ((state & StateNonZero) == 0)
                        {
                            if (!parseDecimal)
                            {
                                number.scale = 0;
                            }
                            if ((state & StateDecimal) == 0)
                            {
                                number.sign = false;
                            }
                        }
                        str = p;
                        return true;
                    }
                }
                str = p;
                return false;
            }
 internal static unsafe float ParseSingle(string value, NumberStyles options, NumberFormatInfo numfmt)
 {
     if (value == null)
     {
         throw new ArgumentNullException("value");
     }
     byte* stackBuffer = stackalloc byte[0x72];
     NumberBuffer number = new NumberBuffer(stackBuffer);
     double num = 0.0;
     if (!TryStringToNumber(value, options, ref number, numfmt, false))
     {
         string str = value.Trim();
         if (str.Equals(numfmt.PositiveInfinitySymbol))
         {
             return float.PositiveInfinity;
         }
         if (str.Equals(numfmt.NegativeInfinitySymbol))
         {
             return float.NegativeInfinity;
         }
         if (!str.Equals(numfmt.NaNSymbol))
         {
             throw new FormatException(Environment.GetResourceString("Format_InvalidString"));
         }
         return float.NaN;
     }
     if (!NumberBufferToDouble(number.PackForNative(), ref num))
     {
         throw new OverflowException(Environment.GetResourceString("Overflow_Single"));
     }
     float f = (float) num;
     if (float.IsInfinity(f))
     {
         throw new OverflowException(Environment.GetResourceString("Overflow_Single"));
     }
     return f;
 }
            internal static unsafe string NumberToString(NumberBuffer number, char format, int nMaxDigits, NumberFormatInfo info, bool isDecimal)
            {
                int nMinDigits = -1;

                StringBuilder sb = new StringBuilder(MIN_SB_BUFFER_SIZE);

                switch (format)
                {
                    case 'C':
                    case 'c':
                        {
                            nMinDigits = nMaxDigits >= 0 ? nMaxDigits : info.CurrencyDecimalDigits;
                            if (nMaxDigits < 0)
                                nMaxDigits = info.CurrencyDecimalDigits;

                            RoundNumber(ref number, number.scale + nMaxDigits); // Don't change this line to use digPos since digCount could have its sign changed.

                            FormatCurrency(sb, number, nMinDigits, nMaxDigits, info);

                            break;
                        }

                    case 'F':
                    case 'f':
                        {
                            if (nMaxDigits < 0)
                                nMaxDigits = nMinDigits = info.NumberDecimalDigits;
                            else
                                nMinDigits = nMaxDigits;

                            RoundNumber(ref number, number.scale + nMaxDigits);

                            if (number.sign)
                                sb.Append(info.NegativeSign);

                            FormatFixed(sb, number, nMinDigits, nMaxDigits, info, null, info.NumberDecimalSeparator, null);

                            break;
                        }

                    case 'N':
                    case 'n':
                        {
                            if (nMaxDigits < 0)
                                nMaxDigits = nMinDigits = info.NumberDecimalDigits; // Since we are using digits in our calculation
                            else
                                nMinDigits = nMaxDigits;

                            RoundNumber(ref number, number.scale + nMaxDigits);

                            FormatNumber(sb, number, nMinDigits, nMaxDigits, info);

                            break;
                        }

                    case 'E':
                    case 'e':
                        {
                            if (nMaxDigits < 0)
                                nMaxDigits = nMinDigits = 6;
                            else
                                nMinDigits = nMaxDigits;
                            nMaxDigits++;

                            RoundNumber(ref number, nMaxDigits);

                            if (number.sign)
                                sb.Append(info.NegativeSign);

                            FormatScientific(sb, number, nMinDigits, nMaxDigits, info, format);

                            break;
                        }

                    case 'G':
                    case 'g':
                        {
                            bool enableRounding = true;
                            if (nMaxDigits < 1)
                            {
                                if (isDecimal && (nMaxDigits == -1))
                                {
                                    // Default to 29 digits precision only for G formatting without a precision specifier
                                    // This ensures that the PAL code pads out to the correct place even when we use the default precision
                                    nMaxDigits = nMinDigits = DECIMAL_PRECISION;
                                    enableRounding = false;  // Turn off rounding for ECMA compliance to output trailing 0's after decimal as significant
                                }
                                else
                                {
                                    // This ensures that the PAL code pads out to the correct place even when we use the default precision
                                    nMaxDigits = nMinDigits = number.precision;
                                }
                            }
                            else
                                nMinDigits = nMaxDigits;

                            if (enableRounding) // Don't round for G formatting without precision
                                RoundNumber(ref number, nMaxDigits); // This also fixes up the minus zero case
                            else
                            {
                                if (isDecimal && (number.digits[0] == 0))
                                {
                                    // Minus zero should be formatted as 0
                                    number.sign = false;
                                }
                            }

                            if (number.sign)
                                sb.Append(info.NegativeSign);

                            FormatGeneral(sb, number, nMinDigits, nMaxDigits, info, (char)(format - ('G' - 'E')), !enableRounding);

                            break;
                        }

                    case 'P':
                    case 'p':
                        {
                            if (nMaxDigits < 0)
                                nMaxDigits = nMinDigits = info.PercentDecimalDigits;
                            else
                                nMinDigits = nMaxDigits;
                            number.scale += 2;

                            RoundNumber(ref number, number.scale + nMaxDigits);

                            FormatPercent(sb, number, nMinDigits, nMaxDigits, info);

                            break;
                        }

                    default:
                        throw new FormatException(SR.Argument_BadFormatSpecifier);
                }

                return sb.ToString();
            }
 private static unsafe void StringToNumber(string str, NumberStyles options, ref NumberBuffer number, NumberFormatInfo info, bool parseDecimal)
 {
     if (str == null)
     {
         throw new ArgumentNullException("String");
     }
     fixed (char* str2 = ((char*) str))
     {
         char* chPtr = str2;
         char* chPtr2 = chPtr;
         if (!ParseNumber(ref chPtr2, options, ref number, null, info, parseDecimal) || ((((long) ((chPtr2 - chPtr) / 2)) < str.Length) && !TrailingZeros(str, (int) ((long) ((chPtr2 - chPtr) / 2)))))
         {
             throw new FormatException(Environment.GetResourceString("Format_InvalidString"));
         }
     }
 }
            private static void FormatNumber(StringBuilder sb, NumberBuffer number, int nMinDigits, int nMaxDigits, NumberFormatInfo info)
            {
                string fmt = number.sign ?
                    s_negNumberFormats[info.NumberNegativePattern] :
                    s_posNumberFormat;

                foreach (char ch in fmt)
                {
                    switch (ch)
                    {
                        case '#':
                            FormatFixed(sb, number, nMinDigits, nMaxDigits, info, info.NumberGroupSizes, info.NumberDecimalSeparator, info.NumberGroupSeparator);
                            break;
                        case '-':
                            sb.Append(info.NegativeSign);
                            break;
                        default:
                            sb.Append(ch);
                            break;
                    }
                }
            }
 internal static unsafe bool TryParseSingle(string value, NumberStyles options, NumberFormatInfo numfmt, out float result)
 {
     byte* stackBuffer = stackalloc byte[0x72];
     NumberBuffer number = new NumberBuffer(stackBuffer);
     result = 0f;
     double num = 0.0;
     if (!TryStringToNumber(value, options, ref number, numfmt, false))
     {
         return false;
     }
     if (!NumberBufferToDouble(number.PackForNative(), ref num))
     {
         return false;
     }
     float f = (float) num;
     if (float.IsInfinity(f))
     {
         return false;
     }
     result = f;
     return true;
 }
Exemple #47
0
        //public static unsafe void Dragon4Half(Half value, int cutoffNumber, bool isSignificantDigits, ref NumberBuffer number)
        //{
        //    Half v = Half.IsNegative(value) ? Half.Negate(value) : value;

        //    Debug.Assert((double)v > 0.0);
        //    Debug.Assert(Half.IsFinite(v));

        //    ushort mantissa = ExtractFractionAndBiasedExponent(value, out int exponent);

        //    uint mantissaHighBitIdx;
        //    bool hasUnequalMargins = false;

        //    if ((mantissa >> DiyFp.HalfImplicitBitIndex) != 0)
        //    {
        //        mantissaHighBitIdx = DiyFp.HalfImplicitBitIndex;
        //        hasUnequalMargins = (mantissa == (1U << DiyFp.HalfImplicitBitIndex));
        //    }
        //    else
        //    {
        //        Debug.Assert(mantissa != 0);
        //        mantissaHighBitIdx = (uint)BitOperations.Log2(mantissa);
        //    }

        //    int length = (int)(Dragon4(mantissa, exponent, mantissaHighBitIdx, hasUnequalMargins, cutoffNumber, isSignificantDigits, number.Digits, out int decimalExponent));

        //    number.Scale = decimalExponent + 1;
        //    number.Digits[length] = (byte)('\0');
        //    number.DigitsCount = length;
        //}

        public static void Dragon4Single(float value, int cutoffNumber, bool isSignificantDigits, ref NumberBuffer number)
        {
            var v = value.IsNegative() ? -value : value;

            Debug.Assert(v > 0);
            Debug.Assert(v.IsFinite());

            var mantissa = ExtractFractionAndBiasedExponent(value, out var exponent);

            uint mantissaHighBitIdx;
            var  hasUnequalMargins = false;

            if (mantissa >> DiyFp.SingleImplicitBitIndex != 0)
            {
                mantissaHighBitIdx = DiyFp.SingleImplicitBitIndex;
                hasUnequalMargins  = mantissa == 1U << DiyFp.SingleImplicitBitIndex;
            }
            else
            {
                Debug.Assert(mantissa != 0);
                mantissaHighBitIdx = (uint)BitOperations.Log2(mantissa);
            }

            var length = (int)Dragon4(mantissa, exponent, mantissaHighBitIdx, hasUnequalMargins, cutoffNumber, isSignificantDigits, number.DigitsMut, out var decimalExponent);

            number.Scale             = decimalExponent + 1;
            number.DigitsMut[length] = (byte)'\0';
            number.DigitsCount       = length;
        }
 internal static bool TryStringToNumber(string str, NumberStyles options, ref NumberBuffer number, NumberFormatInfo numfmt, bool parseDecimal)
 {
     return TryStringToNumber(str, options, ref number, null, numfmt, parseDecimal);
 }
        public static unsafe bool NumberBufferToDecimal(ref NumberBuffer number, ref decimal value)
        {
            MutableDecimal d = new MutableDecimal();

            byte *p = number.UnsafeDigits;
            int   e = number.Scale;

            if (*p == 0)
            {
                // To avoid risking an app-compat issue with pre 4.5 (where some app was illegally using Reflection to examine the internal scale bits), we'll only force
                // the scale to 0 if the scale was previously positive (previously, such cases were unparsable to a bug.)
                if (e > 0)
                {
                    e = 0;
                }
            }
            else
            {
                if (e > DECIMAL_PRECISION)
                {
                    return(false);
                }

                while (((e > 0) || ((*p != 0) && (e > -28))) &&
                       ((d.High < 0x19999999) || ((d.High == 0x19999999) &&
                                                  ((d.Mid < 0x99999999) || ((d.Mid == 0x99999999) &&
                                                                            ((d.Low < 0x99999999) || ((d.Low == 0x99999999) &&
                                                                                                      (*p <= '5'))))))))
                {
                    DecimalDecCalc.DecMul10(ref d);
                    if (*p != 0)
                    {
                        DecimalDecCalc.DecAddInt32(ref d, (uint)(*p++ - '0'));
                    }
                    e--;
                }

                if (*p++ >= '5')
                {
                    bool round = true;
                    if ((*(p - 1) == '5') && ((*(p - 2) % 2) == 0))
                    {
                        // Check if previous digit is even, only if the when we are unsure whether hows to do
                        // Banker's rounding. For digits > 5 we will be rounding up anyway.
                        int count = 20; // Look at the next 20 digits to check to round
                        while ((*p == '0') && (count != 0))
                        {
                            p++;
                            count--;
                        }
                        if ((*p == '\0') || (count == 0))
                        {
                            round = false;// Do nothing
                        }
                    }

                    if (round)
                    {
                        DecimalDecCalc.DecAddInt32(ref d, 1);
                        if ((d.High | d.Mid | d.Low) == 0)
                        {
                            // If we got here, the magnitude portion overflowed and wrapped back to 0 as the magnitude was already at the MaxValue point:
                            //
                            //     79,228,162,514,264,337,593,543,950,335e+X
                            //
                            // Manually force it to the correct result:
                            //
                            //      7,922,816,251,426,433,759,354,395,034e+(X+1)
                            //
                            // This code path can be reached by trying to parse the following as a Decimal:
                            //
                            //      0.792281625142643375935439503355e28
                            //

                            d.High = 0x19999999;
                            d.Mid  = 0x99999999;
                            d.Low  = 0x9999999A;
                            e++;
                        }
                    }
                }
            }

            if (e > 0)
            {
                return(false); // Rounding may have caused its own overflow. For example, parsing "0.792281625142643375935439503355e29" will get here.
            }
            if (e <= -DECIMAL_PRECISION)
            {
                // Parsing a large scale zero can give you more precision than fits in the decimal.
                // This should only happen for actual zeros or very small numbers that round to zero.
                d.High  = 0;
                d.Low   = 0;
                d.Mid   = 0;
                d.Scale = DECIMAL_PRECISION - 1;
            }
            else
            {
                d.Scale = -e;
            }
            d.IsNegative = number.IsNegative;

            value = Unsafe.As <MutableDecimal, decimal>(ref d);
            return(true);
        }
            private static unsafe void FormatGeneral(StringBuilder sb, NumberBuffer number, int nMinDigits, int nMaxDigits, NumberFormatInfo info, char expChar, bool bSuppressScientific)
            {
                int digPos = number.scale;
                bool scientific = false;

                if (!bSuppressScientific)
                {
                    // Don't switch to scientific notation
                    if (digPos > nMaxDigits || digPos < -3)
                    {
                        digPos = 1;
                        scientific = true;
                    }
                }

                char* dig = number.digits;

                if (digPos > 0)
                {
                    do
                    {
                        sb.Append((*dig != 0) ? *dig++ : '0');
                    } while (--digPos > 0);
                }
                else
                {
                    sb.Append('0');
                }

                if (*dig != 0 || digPos < 0)
                {
                    sb.Append(info.NumberDecimalSeparator);

                    while (digPos < 0)
                    {
                        sb.Append('0');
                        digPos++;
                    }

                    while (*dig != 0)
                        sb.Append(*dig++);
                }

                if (scientific)
                    FormatExponent(sb, info, number.scale - 1, expChar, 2, true);
            }
            public static String FormatUInt64(ulong value, String format, IFormatProvider provider)
            {
                NumberFormatInfo info = provider == null ? NumberFormatInfo.CurrentInfo : NumberFormatInfo.GetInstance(provider);

                int digits;
                char fmt = ParseFormatSpecifier(format, out digits);

                // ANDing fmt with FFDF has the effect of uppercasing the character because we've removed the bit
                // that marks lower-case.
                switch (fmt)
                {
                    case 'G':
                    case 'g':
                        if (digits > 0)
                        {
                            NumberBuffer number = new NumberBuffer();
                            UInt64ToNumber(value, ref number);
                            if (fmt != 0)
                                return NumberToString(number, fmt, digits, info, false);
                            return NumberToStringFormat(number, format, info);
                        }
                        // fall through
                        goto case 'D';

                    case 'D':
                    case 'd':
                        return UInt64ToDecStr(value, digits);

                    case 'X':
                    case 'x':
                        // The fmt-(X-A+10) hack has the effect of dictating whether we produce uppercase or lowercase
                        // hex numbers for a-f. 'X' as the fmt code produces uppercase. 'x' as the format code
                        // produces lowercase.
                        return Int64ToHexStr((long)value, (char)(fmt - ('X' - 'A' + 10)), digits);

                    default:
                        {
                            NumberBuffer number = new NumberBuffer();
                            UInt64ToNumber(value, ref number);
                            if (fmt != 0)
                                return NumberToString(number, fmt, digits, info, false);
                            return NumberToStringFormat(number, format, info);
                        }
                }
            }
            internal static unsafe string NumberToStringFormat(NumberBuffer number, string format, NumberFormatInfo info)
            {
                int digitCount;
                int decimalPos;
                int firstDigit;
                int lastDigit;
                int digPos;
                bool scientific;
                int thousandPos;
                int thousandCount = 0;
                bool thousandSeps;
                int scaleAdjust;
                int adjust;

                int section;
                int src;
                char* dig = number.digits;
                char ch;

                section = FindSection(format, dig[0] == 0 ? 2 : number.sign ? 1 : 0);

                while (true)
                {
                    digitCount = 0;
                    decimalPos = -1;
                    firstDigit = 0x7FFFFFFF;
                    lastDigit = 0;
                    scientific = false;
                    thousandPos = -1;
                    thousandSeps = false;
                    scaleAdjust = 0;
                    src = section;

                    fixed (char* pFormat = format)
                    {
                        while ((ch = pFormat[src++]) != 0 && ch != ';')
                        {
                            switch (ch)
                            {
                                case '#':
                                    digitCount++;
                                    break;
                                case '0':
                                    if (firstDigit == 0x7FFFFFFF)
                                        firstDigit = digitCount;
                                    digitCount++;
                                    lastDigit = digitCount;
                                    break;
                                case '.':
                                    if (decimalPos < 0)
                                        decimalPos = digitCount;
                                    break;
                                case ',':
                                    if (digitCount > 0 && decimalPos < 0)
                                    {
                                        if (thousandPos >= 0)
                                        {
                                            if (thousandPos == digitCount)
                                            {
                                                thousandCount++;
                                                break;
                                            }
                                            thousandSeps = true;
                                        }
                                        thousandPos = digitCount;
                                        thousandCount = 1;
                                    }
                                    break;
                                case '%':
                                    scaleAdjust += 2;
                                    break;
                                case '\x2030':
                                    scaleAdjust += 3;
                                    break;
                                case '\'':
                                case '"':
                                    while (pFormat[src] != 0 && pFormat[src++] != ch)
                                        ;
                                    break;
                                case '\\':
                                    if (pFormat[src] != 0)
                                        src++;
                                    break;
                                case 'E':
                                case 'e':
                                    if (pFormat[src] == '0' || ((pFormat[src] == '+' || pFormat[src] == '-') && pFormat[src + 1] == '0'))
                                    {
                                        while (pFormat[++src] == '0')
                                            ;
                                        scientific = true;
                                    }
                                    break;
                            }
                        }
                    }

                    if (decimalPos < 0)
                        decimalPos = digitCount;

                    if (thousandPos >= 0)
                    {
                        if (thousandPos == decimalPos)
                            scaleAdjust -= thousandCount * 3;
                        else
                            thousandSeps = true;
                    }

                    if (dig[0] != 0)
                    {
                        number.scale += scaleAdjust;
                        int pos = scientific ? digitCount : number.scale + digitCount - decimalPos;
                        RoundNumber(ref number, pos);
                        if (dig[0] == 0)
                        {
                            src = FindSection(format, 2);
                            if (src != section)
                            {
                                section = src;
                                continue;
                            }
                        }
                    }
                    else
                    {
                        number.sign = false;   // We need to format -0 without the sign set.
                        number.scale = 0;      // Decimals with scale ('0.00') should be rounded.
                    }

                    break;
                }

                firstDigit = firstDigit < decimalPos ? decimalPos - firstDigit : 0;
                lastDigit = lastDigit > decimalPos ? decimalPos - lastDigit : 0;
                if (scientific)
                {
                    digPos = decimalPos;
                    adjust = 0;
                }
                else
                {
                    digPos = number.scale > decimalPos ? number.scale : decimalPos;
                    adjust = number.scale - decimalPos;
                }
                src = section;

                // Adjust can be negative, so we make this an int instead of an unsigned int.
                // Adjust represents the number of characters over the formatting e.g. format string is "0000" and you are trying to
                // format 100000 (6 digits). Means adjust will be 2. On the other hand if you are trying to format 10 adjust will be
                // -2 and we'll need to fixup these digits with 0 padding if we have 0 formatting as in this example.
                int[] thousandsSepPos = new int[4];
                int thousandsSepCtr = -1;

                if (thousandSeps)
                {
                    // We need to precompute this outside the number formatting loop
                    if (info.NumberGroupSeparator.Length > 0)
                    {
                        // We need this array to figure out where to insert the thousands separator. We would have to traverse the string
                        // backwards. PIC formatting always traverses forwards. These indices are precomputed to tell us where to insert
                        // the thousands separator so we can get away with traversing forwards. Note we only have to compute up to digPos.
                        // The max is not bound since you can have formatting strings of the form "000,000..", and this
                        // should handle that case too.

                        int[] groupDigits = info.NumberGroupSizes;

                        int groupSizeIndex = 0;     // Index into the groupDigits array.
                        int groupTotalSizeCount = 0;
                        int groupSizeLen = groupDigits.Length;    // The length of groupDigits array.
                        if (groupSizeLen != 0)
                            groupTotalSizeCount = groupDigits[groupSizeIndex];   // The current running total of group size.
                        int groupSize = groupTotalSizeCount;

                        int totalDigits = digPos + ((adjust < 0) ? adjust : 0); // Actual number of digits in o/p
                        int numDigits = (firstDigit > totalDigits) ? firstDigit : totalDigits;
                        while (numDigits > groupTotalSizeCount)
                        {
                            if (groupSize == 0)
                                break;
                            ++thousandsSepCtr;
                            if (thousandsSepCtr >= thousandsSepPos.Length)
                                Array.Resize(ref thousandsSepPos, thousandsSepPos.Length * 2);

                            thousandsSepPos[thousandsSepCtr] = groupTotalSizeCount;
                            if (groupSizeIndex < groupSizeLen - 1)
                            {
                                groupSizeIndex++;
                                groupSize = groupDigits[groupSizeIndex];
                            }
                            groupTotalSizeCount += groupSize;
                        }
                    }
                }

                StringBuilder sb = new StringBuilder(MIN_SB_BUFFER_SIZE);

                if (number.sign && section == 0)
                    sb.Append(info.NegativeSign);

                bool decimalWritten = false;

                fixed (char* pFormat = format)
                {
                    char* cur = dig;

                    while ((ch = pFormat[src++]) != 0 && ch != ';')
                    {
                        if (adjust > 0)
                        {
                            switch (ch)
                            {
                                case '#':
                                case '0':
                                case '.':
                                    while (adjust > 0)
                                    {
                                        // digPos will be one greater than thousandsSepPos[thousandsSepCtr] since we are at
                                        // the character after which the groupSeparator needs to be appended.
                                        sb.Append(*cur != 0 ? *cur++ : '0');
                                        if (thousandSeps && digPos > 1 && thousandsSepCtr >= 0)
                                        {
                                            if (digPos == thousandsSepPos[thousandsSepCtr] + 1)
                                            {
                                                sb.Append(info.NumberGroupSeparator);
                                                thousandsSepCtr--;
                                            }
                                        }
                                        digPos--;
                                        adjust--;
                                    }
                                    break;
                            }
                        }

                        switch (ch)
                        {
                            case '#':
                            case '0':
                                {
                                    if (adjust < 0)
                                    {
                                        adjust++;
                                        ch = digPos <= firstDigit ? '0' : '\0';
                                    }
                                    else
                                    {
                                        ch = *cur != 0 ? *cur++ : digPos > lastDigit ? '0' : '\0';
                                    }
                                    if (ch != 0)
                                    {
                                        sb.Append(ch);
                                        if (thousandSeps && digPos > 1 && thousandsSepCtr >= 0)
                                        {
                                            if (digPos == thousandsSepPos[thousandsSepCtr] + 1)
                                            {
                                                sb.Append(info.NumberGroupSeparator);
                                                thousandsSepCtr--;
                                            }
                                        }
                                    }

                                    digPos--;
                                    break;
                                }
                            case '.':
                                {
                                    if (digPos != 0 || decimalWritten)
                                    {
                                        // For compatibility, don't echo repeated decimals
                                        break;
                                    }
                                    // If the format has trailing zeros or the format has a decimal and digits remain
                                    if (lastDigit < 0 || (decimalPos < digitCount && *cur != 0))
                                    {
                                        sb.Append(info.NumberDecimalSeparator);
                                        decimalWritten = true;
                                    }
                                    break;
                                }
                            case '\x2030':
                                sb.Append(info.PerMilleSymbol);
                                break;
                            case '%':
                                sb.Append(info.PercentSymbol);
                                break;
                            case ',':
                                break;
                            case '\'':
                            case '"':
                                while (pFormat[src] != 0 && pFormat[src] != ch)
                                    sb.Append(pFormat[src++]);
                                if (pFormat[src] != 0)
                                    src++;
                                break;
                            case '\\':
                                if (pFormat[src] != 0)
                                    sb.Append(pFormat[src++]);
                                break;
                            case 'E':
                            case 'e':
                                {
                                    bool positiveSign = false;
                                    int i = 0;
                                    if (scientific)
                                    {
                                        if (pFormat[src] == '0')
                                        {
                                            // Handles E0, which should format the same as E-0
                                            i++;
                                        }
                                        else if (pFormat[src] == '+' && pFormat[src + 1] == '0')
                                        {
                                            // Handles E+0
                                            positiveSign = true;
                                        }
                                        else if (pFormat[src] == '-' && pFormat[src + 1] == '0')
                                        {
                                            // Handles E-0
                                            // Do nothing, this is just a place holder s.t. we don't break out of the loop.
                                        }
                                        else
                                        {
                                            sb.Append(ch);
                                            break;
                                        }

                                        while (pFormat[++src] == '0')
                                            i++;
                                        if (i > 10)
                                            i = 10;

                                        int exp = dig[0] == 0 ? 0 : number.scale - decimalPos;
                                        FormatExponent(sb, info, exp, ch, i, positiveSign);
                                        scientific = false;
                                    }
                                    else
                                    {
                                        sb.Append(ch); // Copy E or e to output
                                        if (pFormat[src] == '+' || pFormat[src] == '-')
                                            sb.Append(pFormat[src++]);
                                        while (pFormat[src] == '0')
                                            sb.Append(pFormat[src++]);
                                    }
                                    break;
                                }
                            default:
                                sb.Append(ch);
                                break;
                        }
                    }
                }

                return sb.ToString();
            }
        private static bool TryFormatDecimalG(ref NumberBuffer number, Span <byte> destination, out int bytesWritten)
        {
            int scale = number.Scale;
            ReadOnlySpan <byte> digits = number.Digits;
            int numDigits = number.NumDigits;

            bool isFraction = scale < numDigits;
            int  numBytesNeeded;

            if (isFraction)
            {
                numBytesNeeded = numDigits + 1;  // A fraction. Must include one for the decimal point.
                if (scale <= 0)
                {
                    numBytesNeeded += 1 + (-scale); // A fraction of the form 0.ddd. Need to emit the non-stored 0 before the decimal point plus (-scale) leading 0's after the decimal point.
                }
            }
            else
            {
                numBytesNeeded = ((scale <= 0) ? 1 : scale); // An integral. Just emit the digits before the decimal point (minimum 1) and no decimal point.
            }

            if (number.IsNegative)
            {
                numBytesNeeded++; // And the minus sign.
            }

            if (destination.Length < numBytesNeeded)
            {
                bytesWritten = 0;
                return(false);
            }

            int srcIndex = 0;
            int dstIndex = 0;

            if (number.IsNegative)
            {
                destination[dstIndex++] = Utf8Constants.Minus;
            }

            //
            // Emit digits before the decimal point.
            //
            if (scale <= 0)
            {
                destination[dstIndex++] = (byte)'0';  // The integer portion is 0 and not stored. The formatter, however, needs to emit it.
            }
            else
            {
                while (srcIndex < scale)
                {
                    byte digit = digits[srcIndex];
                    if (digit == 0)
                    {
                        int numTrailingZeroes = scale - srcIndex;
                        for (int i = 0; i < numTrailingZeroes; i++)
                        {
                            destination[dstIndex++] = (byte)'0';
                        }
                        break;
                    }

                    destination[dstIndex++] = digit;
                    srcIndex++;
                }
            }

            if (isFraction)
            {
                destination[dstIndex++] = Utf8Constants.Period;

                //
                // Emit digits after the decimal point.
                //
                if (scale < 0)
                {
                    int numLeadingZeroesToEmit = -scale;
                    for (int i = 0; i < numLeadingZeroesToEmit; i++)
                    {
                        destination[dstIndex++] = (byte)'0';
                    }
                }

                byte digit;
                while ((digit = digits[srcIndex++]) != 0)
                {
                    destination[dstIndex++] = digit;
                }
            }

            Debug.Assert(dstIndex == numBytesNeeded);

            bytesWritten = numBytesNeeded;
            return(true);
        }
            internal static unsafe bool TryStringToNumber(string str, NumberStyles options, ref NumberBuffer number, StringBuilder sb, NumberFormatInfo numfmt, bool parseDecimal)
            {
                if (str == null)
                {
                    return false;
                }
                Debug.Assert(numfmt != null);

                fixed (char* stringPointer = str)
                {
                    char* p = stringPointer;
                    if (!ParseNumber(ref p, options, ref number, sb, numfmt, parseDecimal)
                        || (p - stringPointer < str.Length && !TrailingZeros(str, (int)(p - stringPointer))))
                    {
                        return false;
                    }
                }

                return true;
            }
        private static bool TryFormatDecimalE(ref NumberBuffer number, Span <byte> buffer, out int bytesWritten, byte precision, byte exponentSymbol)
        {
            const int NumExponentDigits = 3;

            int scale = number.Scale;
            ReadOnlySpan <byte> digits = number.Digits;

            int numBytesNeeded =
                ((number.IsNegative) ? 1 : 0)              // minus sign
                + 1                                        // digits before the decimal point (exactly 1)
                + ((precision == 0) ? 0 : (precision + 1)) // period and the digits after the decimal point
                + 2                                        // 'E' or 'e' followed by '+' or '-'
                + NumExponentDigits;                       // exponent digits

            if (buffer.Length < numBytesNeeded)
            {
                bytesWritten = 0;
                return(false);
            }

            int dstIndex = 0;
            int srcIndex = 0;

            if (number.IsNegative)
            {
                buffer[dstIndex++] = Utf8Constants.Minus;
            }

            //
            // Emit exactly one digit before the decimal point.
            //
            int  exponent;
            byte firstDigit = digits[srcIndex];

            if (firstDigit == 0)
            {
                buffer[dstIndex++] = (byte)'0';  // Special case: number before the decimal point is exactly 0: Number does not store the zero in this case.
                exponent           = 0;
            }
            else
            {
                buffer[dstIndex++] = firstDigit;
                srcIndex++;
                exponent = scale - 1;
            }

            if (precision > 0)
            {
                buffer[dstIndex++] = Utf8Constants.Period;

                //
                // Emit digits after the decimal point.
                //
                int numDigitsEmitted = 0;
                while (numDigitsEmitted < precision)
                {
                    byte digit = digits[srcIndex];
                    if (digit == 0)
                    {
                        while (numDigitsEmitted++ < precision)
                        {
                            buffer[dstIndex++] = (byte)'0';
                        }
                        break;
                    }
                    buffer[dstIndex++] = digit;
                    srcIndex++;
                    numDigitsEmitted++;
                }
            }

            // Emit the exponent symbol
            buffer[dstIndex++] = exponentSymbol;
            if (exponent >= 0)
            {
                buffer[dstIndex++] = Utf8Constants.Plus;
            }
            else
            {
                buffer[dstIndex++] = Utf8Constants.Minus;
                exponent           = -exponent;
            }

            Debug.Assert(exponent < Number.DECIMAL_PRECISION, "If you're trying to reuse this routine for double/float, you'll need to review the code carefully for Decimal-specific assumptions.");

            // Emit exactly three digits for the exponent.
            buffer[dstIndex++] = (byte)'0'; // The exponent for Decimal can never exceed 28 (let alone 99)
            buffer[dstIndex++] = (byte)((exponent / 10) + '0');
            buffer[dstIndex++] = (byte)((exponent % 10) + '0');

            Debug.Assert(dstIndex == numBytesNeeded);
            bytesWritten = numBytesNeeded;
            return(true);
        }
            private static unsafe void FormatFixed(StringBuilder sb, NumberBuffer number, int nMinDigits, int nMaxDigits, NumberFormatInfo info, int[] groupDigits, string sDecimal, string sGroup)
            {
                int digPos = number.scale;
                char* dig = number.digits;
                int digLength = wcslen(dig);

                if (digPos > 0)
                {
                    if (groupDigits != null)
                    {
                        int groupSizeIndex = 0;                             // Index into the groupDigits array.
                        int groupSizeCount = groupDigits[groupSizeIndex];   // The current total of group size.
                        int groupSizeLen = groupDigits.Length;              // The length of groupDigits array.
                        int bufferSize = digPos;                            // The length of the result buffer string.
                        int groupSeparatorLen = sGroup.Length;              // The length of the group separator string.
                        int groupSize = 0;                                  // The current group size.

                        // Find out the size of the string buffer for the result.
                        if (groupSizeLen != 0) // You can pass in 0 length arrays
                        {
                            while (digPos > groupSizeCount)
                            {
                                groupSize = groupDigits[groupSizeIndex];
                                if (groupSize == 0)
                                    break;

                                bufferSize += groupSeparatorLen;
                                if (groupSizeIndex < groupSizeLen - 1)
                                    groupSizeIndex++;

                                groupSizeCount += groupDigits[groupSizeIndex];
                                if (groupSizeCount < 0 || bufferSize < 0)
                                    throw new ArgumentOutOfRangeException(); // If we overflow
                            }
                            if (groupSizeCount == 0) // If you passed in an array with one entry as 0, groupSizeCount == 0
                                groupSize = 0;
                            else
                                groupSize = groupDigits[0];
                        }

                        char* tmpBuffer = stackalloc char[bufferSize];
                        groupSizeIndex = 0;
                        int digitCount = 0;
                        int digStart;
                        digStart = (digPos < digLength) ? digPos : digLength;
                        char* p = tmpBuffer + bufferSize - 1;
                        for (int i = digPos - 1; i >= 0; i--)
                        {
                            *(p--) = (i < digStart) ? dig[i] : '0';

                            if (groupSize > 0)
                            {
                                digitCount++;
                                if ((digitCount == groupSize) && (i != 0))
                                {
                                    for (int j = groupSeparatorLen - 1; j >= 0; j--)
                                        *(p--) = sGroup[j];

                                    if (groupSizeIndex < groupSizeLen - 1)
                                    {
                                        groupSizeIndex++;
                                        groupSize = groupDigits[groupSizeIndex];
                                    }
                                    digitCount = 0;
                                }
                            }
                        }

                        sb.Append(tmpBuffer, bufferSize);
                        dig += digStart;
                    }
                    else
                    {
                        int digits = Math.Min(digLength, digPos);
                        sb.Append(dig, digits);
                        dig += digits;
                        if (digPos > digLength)
                            sb.Append('0', digPos - digLength);
                    }
                }
                else
                {
                    sb.Append('0');
                }

                if (nMaxDigits > 0)
                {
                    sb.Append(sDecimal);
                    if ((digPos < 0) && (nMaxDigits > 0))
                    {
                        int zeroes = Math.Min(-digPos, nMaxDigits);
                        sb.Append('0', zeroes);
                        digPos += zeroes;
                        nMaxDigits -= zeroes;
                    }

                    while (nMaxDigits > 0)
                    {
                        sb.Append((*dig != 0) ? *dig++ : '0');
                        nMaxDigits--;
                    }
                }
            }
 private static unsafe bool NumberToInt32(ref NumberBuffer number, ref int value)
 {
     int scale = number.scale;
     if ((scale > 10) || (scale < number.precision))
     {
         return false;
     }
     char* digits = number.digits;
     int num2 = 0;
     while (--scale >= 0)
     {
         if (num2 > 0xccccccc)
         {
             return false;
         }
         num2 *= 10;
         if (digits[0] != '\0')
         {
             digits++;
             num2 += digits[0] - '0';
         }
     }
     if (number.sign)
     {
         num2 = -num2;
         if (num2 > 0)
         {
             return false;
         }
     }
     else if (num2 < 0)
     {
         return false;
     }
     value = num2;
     return true;
 }
            private static unsafe void FormatScientific(StringBuilder sb, NumberBuffer number, int nMinDigits, int nMaxDigits, NumberFormatInfo info, char expChar)
            {
                char* dig = number.digits;

                sb.Append((*dig != 0) ? *dig++ : '0');

                if (nMaxDigits != 1) // For E0 we would like to suppress the decimal point
                    sb.Append(info.NumberDecimalSeparator);

                while (--nMaxDigits > 0)
                    sb.Append((*dig != 0) ? *dig++ : '0');

                int e = number.digits[0] == 0 ? 0 : number.scale - 1;
                FormatExponent(sb, info, e, expChar, 3, true);
            }
 private static unsafe bool NumberToInt64(ref NumberBuffer number, ref long value)
 {
     int scale = number.scale;
     if ((scale > 0x13) || (scale < number.precision))
     {
         return false;
     }
     char* digits = number.digits;
     long num2 = 0L;
     while (--scale >= 0)
     {
         if (num2 > 0xcccccccccccccccL)
         {
             return false;
         }
         num2 *= 10L;
         if (digits[0] != '\0')
         {
             digits++;
             num2 += digits[0] - '0';
         }
     }
     if (number.sign)
     {
         num2 = -num2;
         if (num2 > 0L)
         {
             return false;
         }
     }
     else if (num2 < 0L)
     {
         return false;
     }
     value = num2;
     return true;
 }