Some cleanup of the System.Number class (dotnet/coreclr#20619)
authorTanner Gooding <tagoo@outlook.com>
Mon, 29 Oct 2018 18:15:35 +0000 (11:15 -0700)
committerGitHub <noreply@github.com>
Mon, 29 Oct 2018 18:15:35 +0000 (11:15 -0700)
* Formatting Number.Formatting.cs and Number.Parsing.cs

* Removing some duplicated parsing code by having the Parse method call TryParse

* Moving two constants from NumberBuffer to Dragon4

* Rename FloatPrecision to SinglePrecision

* Updating the casing of the NumberBuffer fields

* Updating NumberBuffer to allow taking a custom-sized digit buffer.

* Updating the various NumberBufferLength constants to be the exact needed lengths

* Fixing DoubleNumberBufferLength and SingleNumberBufferLength to account for the rounding digit.

* Fixing TryParseNumber to use the correct maxDigCount

* Ensure the TryParseSingle out result is assigned on success

Commit migrated from https://github.com/dotnet/coreclr/commit/aef0bc27daebb70ccc4bdb9bd46960b7e8b5eb0e

17 files changed:
src/libraries/System.Private.CoreLib/src/System/Byte.cs
src/libraries/System.Private.CoreLib/src/System/Decimal.cs
src/libraries/System.Private.CoreLib/src/System/Double.cs
src/libraries/System.Private.CoreLib/src/System/Int16.cs
src/libraries/System.Private.CoreLib/src/System/Int32.cs
src/libraries/System.Private.CoreLib/src/System/Int64.cs
src/libraries/System.Private.CoreLib/src/System/Number.Dragon4.cs
src/libraries/System.Private.CoreLib/src/System/Number.Formatting.cs
src/libraries/System.Private.CoreLib/src/System/Number.Grisu3.cs
src/libraries/System.Private.CoreLib/src/System/Number.NumberBuffer.cs
src/libraries/System.Private.CoreLib/src/System/Number.NumberToDouble.cs
src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs
src/libraries/System.Private.CoreLib/src/System/SByte.cs
src/libraries/System.Private.CoreLib/src/System/Single.cs
src/libraries/System.Private.CoreLib/src/System/UInt16.cs
src/libraries/System.Private.CoreLib/src/System/UInt32.cs
src/libraries/System.Private.CoreLib/src/System/UInt64.cs

index da1f914..4004cf1 100644 (file)
@@ -160,7 +160,7 @@ namespace System
         {
             result = 0;
             int i;
-            if (!Number.TryParseInt32(s, style, info, out i))
+            if (!Number.TryParseInt32(s, style, info, out i, out _))
             {
                 return false;
             }
index a79a7c3..2d38297 100644 (file)
@@ -488,12 +488,12 @@ namespace System
                 return false;
             }
 
-            return Number.TryParseDecimal(s, NumberStyles.Number, NumberFormatInfo.CurrentInfo, out result);
+            return Number.TryParseDecimal(s, NumberStyles.Number, NumberFormatInfo.CurrentInfo, out result, out _);
         }
 
         public static bool TryParse(ReadOnlySpan<char> s, out decimal result)
         {
-            return Number.TryParseDecimal(s, NumberStyles.Number, NumberFormatInfo.CurrentInfo, out result);
+            return Number.TryParseDecimal(s, NumberStyles.Number, NumberFormatInfo.CurrentInfo, out result, out _);
         }
 
         public static bool TryParse(string s, NumberStyles style, IFormatProvider provider, out decimal result)
@@ -506,13 +506,13 @@ namespace System
                 return false;
             }
 
-            return Number.TryParseDecimal(s, style, NumberFormatInfo.GetInstance(provider), out result);
+            return Number.TryParseDecimal(s, style, NumberFormatInfo.GetInstance(provider), out result, out _);
         }
 
         public static bool TryParse(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider provider, out decimal result)
         {
             NumberFormatInfo.ValidateParseStyleFloatingPoint(style);
-            return Number.TryParseDecimal(s, style, NumberFormatInfo.GetInstance(provider), out result);
+            return Number.TryParseDecimal(s, style, NumberFormatInfo.GetInstance(provider), out result, out _);
         }
 
         // Returns a binary representation of a Decimal. The return value is an
index d85b4bc..5a94551 100644 (file)
@@ -341,7 +341,7 @@ namespace System
 
         private static bool TryParse(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info, out double result)
         {
-            bool success = Number.TryParseDouble(s, style, info, out result);
+            bool success = Number.TryParseDouble(s, style, info, out result, out _);
             if (!success)
             {
                 ReadOnlySpan<char> sTrim = s.Trim();
index 4973299..a88d477 100644 (file)
@@ -201,7 +201,7 @@ namespace System
         {
             result = 0;
             int i;
-            if (!Number.TryParseInt32(s, style, info, out i))
+            if (!Number.TryParseInt32(s, style, info, out i, out _))
             {
                 return false;
             }
index 1d0aefe..5aa9753 100644 (file)
@@ -152,12 +152,12 @@ namespace System
                 return false;
             }
 
-            return Number.TryParseInt32(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result);
+            return Number.TryParseInt32(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result, out _);
         }
 
         public static bool TryParse(ReadOnlySpan<char> s, out int result)
         {
-            return Number.TryParseInt32(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result);
+            return Number.TryParseInt32(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result, out _);
         }
 
         // Parses an integer from a String in the given style. Returns false rather
@@ -173,13 +173,13 @@ namespace System
                 return false;
             }
 
-            return Number.TryParseInt32(s, style, NumberFormatInfo.GetInstance(provider), out result);
+            return Number.TryParseInt32(s, style, NumberFormatInfo.GetInstance(provider), out result, out _);
         }
 
         public static bool TryParse(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider provider, out int result)
         {
             NumberFormatInfo.ValidateParseStyleInteger(style);
-            return Number.TryParseInt32(s, style, NumberFormatInfo.GetInstance(provider), out result);
+            return Number.TryParseInt32(s, style, NumberFormatInfo.GetInstance(provider), out result, out _);
         }
 
         //
index 62c9ffd..78e8352 100644 (file)
@@ -143,12 +143,12 @@ namespace System
                 return false;
             }
 
-            return Number.TryParseInt64(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result);
+            return Number.TryParseInt64(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result, out _);
         }
 
         public static bool TryParse(ReadOnlySpan<char> s, out long result)
         {
-            return Number.TryParseInt64(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result);
+            return Number.TryParseInt64(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result, out _);
         }
 
         public static bool TryParse(string s, NumberStyles style, IFormatProvider provider, out long result)
@@ -161,13 +161,13 @@ namespace System
                 return false;
             }
 
-            return Number.TryParseInt64(s, style, NumberFormatInfo.GetInstance(provider), out result);
+            return Number.TryParseInt64(s, style, NumberFormatInfo.GetInstance(provider), out result, out _);
         }
 
         public static bool TryParse(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider provider, out long result)
         {
             NumberFormatInfo.ValidateParseStyleInteger(style);
-            return Number.TryParseInt64(s, style, NumberFormatInfo.GetInstance(provider), out result);
+            return Number.TryParseInt64(s, style, NumberFormatInfo.GetInstance(provider), out result, out _);
         }
 
         //
index 3edefe3..7c1d480 100644 (file)
@@ -2,16 +2,17 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
-using System.Runtime.InteropServices;
-using System.Runtime.CompilerServices;
-using Internal.Runtime.CompilerServices;
-
 namespace System
 {
     internal static partial class Number
     {
         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/
@@ -149,7 +150,7 @@ namespace System
                 r.Multiply10();
             }
 
-            number.scale = (k - 1);
+            number.Scale = (k - 1);
 
             // This the prerequisite of calling BigInteger.HeuristicDivide().
             BigInteger.PrepareHeuristicDivide(ref r, ref s);
@@ -171,7 +172,7 @@ namespace System
                     break;
                 }
 
-                number.digits[digitsNum] = (char)('0' + currentDigit);
+                number.Digits[digitsNum] = (char)('0' + currentDigit);
                 digitsNum++;
 
                 r.Multiply10();
@@ -199,7 +200,7 @@ namespace System
 
             if (isRoundDown)
             {
-                number.digits[digitsNum] = (char)('0' + currentDigit);
+                number.Digits[digitsNum] = (char)('0' + currentDigit);
                 digitsNum++;
             }
             else
@@ -218,7 +219,7 @@ namespace System
                             // Output 1 at the next highest exponent
                             *pCurrentDigit = '1';
                             digitsNum++;
-                            number.scale += 1;
+                            number.Scale += 1;
                             break;
                         }
 
@@ -244,14 +245,14 @@ namespace System
 
             while (digitsNum < precision)
             {
-                number.digits[digitsNum] = '0';
+                number.Digits[digitsNum] = '0';
                 digitsNum++;
             }
 
-            number.digits[precision] = '\0';
+            number.Digits[precision] = '\0';
 
-            number.scale++;
-            number.sign = double.IsNegative(value);
+            number.Scale++;
+            number.Sign = double.IsNegative(value);
         }
     }
 }
index 51ecee3..76f0469 100644 (file)
@@ -243,7 +243,7 @@ namespace System
     internal static partial class Number
     {
         internal const int DecimalPrecision = 29; // Decimal.DecCalc also uses this value
-        private const int FloatPrecision = 7;
+        private const int SinglePrecision = 7;
         private const int DoublePrecision = 15;
         private const int ScaleNAN = unchecked((int)0x80000000);
         private const int ScaleINF = 0x7FFFFFFF;
@@ -285,11 +285,13 @@ namespace System
             "(#)", "-#", "- #", "#-", "# -",
         };
 
-        public static string FormatDecimal(decimal value, ReadOnlySpan<char> format, NumberFormatInfo info)
+        public static unsafe string FormatDecimal(decimal value, ReadOnlySpan<char> format, NumberFormatInfo info)
         {
             char fmt = ParseFormatSpecifier(format, out int digits);
 
-            NumberBuffer number = default;
+            char* pDigits = stackalloc char[DecimalNumberBufferLength];
+            NumberBuffer number = new NumberBuffer(NumberBufferKind.Decimal, pDigits, DecimalNumberBufferLength);
+
             DecimalToNumber(ref value, ref number);
 
             ValueStringBuilder sb;
@@ -311,11 +313,13 @@ namespace System
             return sb.ToString();
         }
 
-        public static bool TryFormatDecimal(decimal value, ReadOnlySpan<char> format, NumberFormatInfo info, Span<char> destination, out int charsWritten)
+        public static unsafe bool TryFormatDecimal(decimal value, ReadOnlySpan<char> format, NumberFormatInfo info, Span<char> destination, out int charsWritten)
         {
             char fmt = ParseFormatSpecifier(format, out int digits);
 
-            NumberBuffer number = default;
+            char* pDigits = stackalloc char[DecimalNumberBufferLength];
+            NumberBuffer number = new NumberBuffer(NumberBufferKind.Decimal, pDigits, DecimalNumberBufferLength);
+
             DecimalToNumber(ref value, ref number);
 
             ValueStringBuilder sb;
@@ -340,9 +344,8 @@ namespace System
         private static unsafe void DecimalToNumber(ref decimal d, ref NumberBuffer number)
         {
             char* buffer = number.GetDigitsPointer();
-            number.precision = DecimalPrecision;
-            number.sign = d.IsNegative;
-            number.kind = NumberBufferKind.Decimal;
+            number.Precision = DecimalPrecision;
+            number.Sign = d.IsNegative;
 
             char* p = buffer + DecimalPrecision;
             while ((d.Mid | d.High) != 0)
@@ -352,7 +355,7 @@ namespace System
             p = UInt32ToDecChars(p, d.Low, 0);
 
             int i = (int)((byte*)(buffer + DecimalPrecision) - (byte*)p) >> 1;
-            number.scale = i - d.Scale;
+            number.Scale = i - d.Scale;
 
             char* dst = number.GetDigitsPointer();
             while (--i >= 0)
@@ -384,44 +387,45 @@ namespace System
         /// Non-null if an existing string can be returned, in which case the builder will be unmodified.
         /// Null if no existing string was returned, in which case the formatted output is in the builder.
         /// </returns>
-        private static string FormatDouble(ref ValueStringBuilder sb, double value, ReadOnlySpan<char> format, NumberFormatInfo info)
+        private static unsafe string FormatDouble(ref ValueStringBuilder sb, double value, ReadOnlySpan<char> format, NumberFormatInfo info)
         {
             char fmt = ParseFormatSpecifier(format, out int digits);
             int precision = DoublePrecision;
-            NumberBuffer number = default;
-            number.kind = NumberBufferKind.Double;
+
+            char* pDigits = stackalloc char[DoubleNumberBufferLength];
+            NumberBuffer number = new NumberBuffer(NumberBufferKind.Double, pDigits, DoubleNumberBufferLength);
 
             switch (fmt)
             {
                 case 'R':
                 case 'r':
+                {
+                    // In order to give numbers that are both friendly to display and round-trippable, we parse the
+                    // number using 15 digits and then determine if it round trips to the same value. If it does, we
+                    // convert that NUMBER to a string, otherwise we reparse using 17 digits and display that.
+                    DoubleToNumber(value, DoublePrecision, ref number);
+                    if (number.Scale == ScaleNAN)
                     {
-                        // In order to give numbers that are both friendly to display and round-trippable, we parse the
-                        // number using 15 digits and then determine if it round trips to the same value. If it does, we
-                        // convert that NUMBER to a string, otherwise we reparse using 17 digits and display that.
-                        DoubleToNumber(value, DoublePrecision, ref number);
-                        if (number.scale == ScaleNAN)
-                        {
-                            return info.NaNSymbol;
-                        }
-                        else if (number.scale == ScaleINF)
-                        {
-                            return number.sign ? info.NegativeInfinitySymbol : info.PositiveInfinitySymbol;
-                        }
-
-                        if (NumberToDouble(ref number) == value)
-                        {
-                            NumberToString(ref sb, ref number, 'G', DoublePrecision, info);
-                        }
-                        else
-                        {
-                            DoubleToNumber(value, 17, ref number);
-                            NumberToString(ref sb, ref number, 'G', 17, info);
-                        }
+                        return info.NaNSymbol;
+                    }
+                    else if (number.Scale == ScaleINF)
+                    {
+                        return number.Sign ? info.NegativeInfinitySymbol : info.PositiveInfinitySymbol;
+                    }
 
-                        return null;
+                    if (NumberToDouble(ref number) == value)
+                    {
+                        NumberToString(ref sb, ref number, 'G', DoublePrecision, info);
+                    }
+                    else
+                    {
+                        DoubleToNumber(value, 17, ref number);
+                        NumberToString(ref sb, ref number, 'G', 17, info);
                     }
 
+                    return null;
+                }
+
                 case 'E':
                 case 'e':
                     // Round values less than E14 to 15 digits
@@ -442,13 +446,13 @@ namespace System
             }
 
             DoubleToNumber(value, precision, ref number);
-            if (number.scale == ScaleNAN)
+            if (number.Scale == ScaleNAN)
             {
                 return info.NaNSymbol;
             }
-            else if (number.scale == ScaleINF)
+            else if (number.Scale == ScaleINF)
             {
-                return number.sign ? info.NegativeInfinitySymbol : info.PositiveInfinitySymbol;
+                return number.Sign ? info.NegativeInfinitySymbol : info.PositiveInfinitySymbol;
             }
 
             if (fmt != 0)
@@ -485,42 +489,43 @@ namespace System
         /// Non-null if an existing string can be returned, in which case the builder will be unmodified.
         /// Null if no existing string was returned, in which case the formatted output is in the builder.
         /// </returns>
-        private static string FormatSingle(ref ValueStringBuilder sb, float value, ReadOnlySpan<char> format, NumberFormatInfo info)
+        private static unsafe string FormatSingle(ref ValueStringBuilder sb, float value, ReadOnlySpan<char> format, NumberFormatInfo info)
         {
             char fmt = ParseFormatSpecifier(format, out int digits);
-            int precision = FloatPrecision;
-            NumberBuffer number = default;
-            number.kind = NumberBufferKind.Double;
+            int precision = SinglePrecision;
+
+            char* pDigits = stackalloc char[SingleNumberBufferLength];
+            NumberBuffer number = new NumberBuffer(NumberBufferKind.Double, pDigits, SingleNumberBufferLength);
 
             switch (fmt)
             {
                 case 'R':
                 case 'r':
+                {
+                    // In order to give numbers that are both friendly to display and round-trippable, we parse the
+                    // number using 7 digits and then determine if it round trips to the same value. If it does, we
+                    // convert that NUMBER to a string, otherwise we reparse using 9 digits and display that.
+                    DoubleToNumber(value, SinglePrecision, ref number);
+                    if (number.Scale == ScaleNAN)
                     {
-                        // In order to give numbers that are both friendly to display and round-trippable, we parse the
-                        // number using 7 digits and then determine if it round trips to the same value. If it does, we
-                        // convert that NUMBER to a string, otherwise we reparse using 9 digits and display that.
-                        DoubleToNumber(value, FloatPrecision, ref number);
-                        if (number.scale == ScaleNAN)
-                        {
-                            return info.NaNSymbol;
-                        }
-                        else if (number.scale == ScaleINF)
-                        {
-                            return number.sign ? info.NegativeInfinitySymbol : info.PositiveInfinitySymbol;
-                        }
+                        return info.NaNSymbol;
+                    }
+                    else if (number.Scale == ScaleINF)
+                    {
+                        return number.Sign ? info.NegativeInfinitySymbol : info.PositiveInfinitySymbol;
+                    }
 
-                        if ((float)NumberToDouble(ref number) == value)
-                        {
-                            NumberToString(ref sb, ref number, 'G', FloatPrecision, info);
-                        }
-                        else
-                        {
-                            DoubleToNumber(value, 9, ref number);
-                            NumberToString(ref sb, ref number, 'G', 9, info);
-                        }
-                        return null;
+                    if ((float)NumberToDouble(ref number) == value)
+                    {
+                        NumberToString(ref sb, ref number, 'G', SinglePrecision, info);
                     }
+                    else
+                    {
+                        DoubleToNumber(value, 9, ref number);
+                        NumberToString(ref sb, ref number, 'G', 9, info);
+                    }
+                    return null;
+                }
 
                 case 'E':
                 case 'e':
@@ -542,13 +547,13 @@ namespace System
             }
 
             DoubleToNumber(value, precision, ref number);
-            if (number.scale == ScaleNAN)
+            if (number.Scale == ScaleNAN)
             {
                 return info.NaNSymbol;
             }
-            else if (number.scale == ScaleINF)
+            else if (number.Scale == ScaleINF)
             {
-                return number.sign ? info.NegativeInfinitySymbol : info.PositiveInfinitySymbol;
+                return number.Sign ? info.NegativeInfinitySymbol : info.PositiveInfinitySymbol;
             }
 
             if (fmt != 0)
@@ -576,7 +581,7 @@ namespace System
             return false;
         }
 
-        public static string FormatInt32(int value, ReadOnlySpan<char> format, IFormatProvider provider)
+        public static unsafe string FormatInt32(int value, ReadOnlySpan<char> format, IFormatProvider provider)
         {
             // Fast path for default format with a non-negative value
             if (value >= 0 && format.Length == 0)
@@ -601,7 +606,10 @@ namespace System
             else
             {
                 NumberFormatInfo info = NumberFormatInfo.GetInstance(provider);
-                NumberBuffer number = default;
+
+                char* pDigits = stackalloc char[Int32NumberBufferLength];
+                NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, Int32NumberBufferLength);
+
                 Int32ToNumber(value, ref number);
                 ValueStringBuilder sb;
                 unsafe
@@ -621,7 +629,7 @@ namespace System
             }
         }
 
-        public static bool TryFormatInt32(int value, ReadOnlySpan<char> format, IFormatProvider provider, Span<char> destination, out int charsWritten)
+        public static unsafe bool TryFormatInt32(int value, ReadOnlySpan<char> format, IFormatProvider provider, Span<char> destination, out int charsWritten)
         {
             // Fast path for default format with a non-negative value
             if (value >= 0 && format.Length == 0)
@@ -646,7 +654,10 @@ namespace System
             else
             {
                 NumberFormatInfo info = NumberFormatInfo.GetInstance(provider);
-                NumberBuffer number = default;
+
+                char* pDigits = stackalloc char[Int32NumberBufferLength];
+                NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, Int32NumberBufferLength);
+
                 Int32ToNumber(value, ref number);
                 ValueStringBuilder sb;
                 unsafe
@@ -666,7 +677,7 @@ namespace System
             }
         }
 
-        public static string FormatUInt32(uint value, ReadOnlySpan<char> format, IFormatProvider provider)
+        public static unsafe string FormatUInt32(uint value, ReadOnlySpan<char> format, IFormatProvider provider)
         {
             // Fast path for default format
             if (format.Length == 0)
@@ -689,7 +700,10 @@ namespace System
             else
             {
                 NumberFormatInfo info = NumberFormatInfo.GetInstance(provider);
-                NumberBuffer number = default;
+
+                char* pDigits = stackalloc char[UInt32NumberBufferLength];
+                NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, UInt32NumberBufferLength);
+
                 UInt32ToNumber(value, ref number);
                 ValueStringBuilder sb;
                 unsafe
@@ -709,7 +723,7 @@ namespace System
             }
         }
 
-        public static bool TryFormatUInt32(uint value, ReadOnlySpan<char> format, IFormatProvider provider, Span<char> destination, out int charsWritten)
+        public static unsafe bool TryFormatUInt32(uint value, ReadOnlySpan<char> format, IFormatProvider provider, Span<char> destination, out int charsWritten)
         {
             // Fast path for default format
             if (format.Length == 0)
@@ -732,7 +746,10 @@ namespace System
             else
             {
                 NumberFormatInfo info = NumberFormatInfo.GetInstance(provider);
-                NumberBuffer number = default;
+
+                char* pDigits = stackalloc char[UInt32NumberBufferLength];
+                NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, UInt32NumberBufferLength);
+
                 UInt32ToNumber(value, ref number);
                 ValueStringBuilder sb;
                 unsafe
@@ -752,7 +769,7 @@ namespace System
             }
         }
 
-        public static string FormatInt64(long value, ReadOnlySpan<char> format, IFormatProvider provider)
+        public static unsafe string FormatInt64(long value, ReadOnlySpan<char> format, IFormatProvider provider)
         {
             // Fast path for default format with a non-negative value
             if (value >= 0 && format.Length == 0)
@@ -778,7 +795,10 @@ namespace System
             else
             {
                 NumberFormatInfo info = NumberFormatInfo.GetInstance(provider);
-                NumberBuffer number = default;
+
+                char* pDigits = stackalloc char[Int64NumberBufferLength];
+                NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, Int64NumberBufferLength);
+
                 Int64ToNumber(value, ref number);
                 ValueStringBuilder sb;
                 unsafe
@@ -798,7 +818,7 @@ namespace System
             }
         }
 
-        public static bool TryFormatInt64(long value, ReadOnlySpan<char> format, IFormatProvider provider, Span<char> destination, out int charsWritten)
+        public static unsafe bool TryFormatInt64(long value, ReadOnlySpan<char> format, IFormatProvider provider, Span<char> destination, out int charsWritten)
         {
             // Fast path for default format with a non-negative value
             if (value >= 0 && format.Length == 0)
@@ -824,7 +844,10 @@ namespace System
             else
             {
                 NumberFormatInfo info = NumberFormatInfo.GetInstance(provider);
-                NumberBuffer number = default;
+
+                char* pDigits = stackalloc char[Int64NumberBufferLength];
+                NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, Int64NumberBufferLength);
+
                 Int64ToNumber(value, ref number);
                 ValueStringBuilder sb;
                 unsafe
@@ -844,7 +867,7 @@ namespace System
             }
         }
 
-        public static string FormatUInt64(ulong value, ReadOnlySpan<char> format, IFormatProvider provider)
+        public static unsafe string FormatUInt64(ulong value, ReadOnlySpan<char> format, IFormatProvider provider)
         {
             // Fast path for default format
             if (format.Length == 0)
@@ -868,7 +891,10 @@ namespace System
             else
             {
                 NumberFormatInfo info = NumberFormatInfo.GetInstance(provider);
-                NumberBuffer number = default;
+
+                char* pDigits = stackalloc char[UInt64NumberBufferLength];
+                NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, UInt64NumberBufferLength);
+
                 UInt64ToNumber(value, ref number);
                 ValueStringBuilder sb;
                 unsafe
@@ -888,7 +914,7 @@ namespace System
             }
         }
 
-        public static bool TryFormatUInt64(ulong value, ReadOnlySpan<char> format, IFormatProvider provider, Span<char> destination, out int charsWritten)
+        public static unsafe bool TryFormatUInt64(ulong value, ReadOnlySpan<char> format, IFormatProvider provider, Span<char> destination, out int charsWritten)
         {
             // Fast path for default format
             if (format.Length == 0)
@@ -912,7 +938,10 @@ namespace System
             else
             {
                 NumberFormatInfo info = NumberFormatInfo.GetInstance(provider);
-                NumberBuffer number = default;
+
+                char* pDigits = stackalloc char[UInt64NumberBufferLength];
+                NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, UInt64NumberBufferLength);
+
                 UInt64ToNumber(value, ref number);
                 ValueStringBuilder sb;
                 unsafe
@@ -935,15 +964,15 @@ namespace System
         [MethodImpl(MethodImplOptions.AggressiveInlining)] // called from only one location
         private static unsafe void Int32ToNumber(int value, ref NumberBuffer number)
         {
-            number.precision = Int32Precision;
+            number.Precision = Int32Precision;
 
             if (value >= 0)
             {
-                number.sign = false;
+                number.Sign = false;
             }
             else
             {
-                number.sign = true;
+                number.Sign = true;
                 value = -value;
             }
 
@@ -951,8 +980,7 @@ namespace System
             char* p = UInt32ToDecChars(buffer + Int32Precision, (uint)value, 0);
             int i = (int)(buffer + Int32Precision - p);
 
-            number.scale = i;
-            number.kind = NumberBufferKind.Integer;
+            number.Scale = i;
 
             char* dst = number.GetDigitsPointer();
             while (--i >= 0)
@@ -1062,14 +1090,13 @@ namespace System
         [MethodImpl(MethodImplOptions.AggressiveInlining)] // called from only one location
         private static unsafe void UInt32ToNumber(uint value, ref NumberBuffer number)
         {
-            number.precision = UInt32Precision;
-            number.sign = false;
+            number.Precision = UInt32Precision;
+            number.Sign = false;
 
             char* buffer = number.GetDigitsPointer();
             char* p = UInt32ToDecChars(buffer + UInt32Precision, value, 0);
             int i = (int)(buffer + UInt32Precision - p);
-            number.scale = i;
-            number.kind = NumberBufferKind.Integer;
+            number.Scale = i;
 
             char* dst = number.GetDigitsPointer();
             while (--i >= 0)
@@ -1174,9 +1201,9 @@ namespace System
         private static unsafe void Int64ToNumber(long input, ref NumberBuffer number)
         {
             ulong value = (ulong)input;
-            number.sign = input < 0;
-            number.precision = Int64Precision;
-            if (number.sign)
+            number.Sign = input < 0;
+            number.Precision = Int64Precision;
+            if (number.Sign)
             {
                 value = (ulong)(-input);
             }
@@ -1188,8 +1215,7 @@ namespace System
             p = UInt32ToDecChars(p, Low32(value), 0);
             int i = (int)(buffer + Int64Precision - p);
 
-            number.scale = i;
-            number.kind = NumberBufferKind.Integer;
+            number.Scale = i;
 
             char* dst = number.GetDigitsPointer();
             while (--i >= 0)
@@ -1319,8 +1345,8 @@ namespace System
 
         private static unsafe void UInt64ToNumber(ulong value, ref NumberBuffer number)
         {
-            number.precision = UInt64Precision;
-            number.sign = false;
+            number.Precision = UInt64Precision;
+            number.Sign = false;
 
             char* buffer = number.GetDigitsPointer();
             char* p = buffer + UInt64Precision;
@@ -1330,8 +1356,7 @@ namespace System
             p = UInt32ToDecChars(p, Low32(value), 0);
             int i = (int)(buffer + UInt64Precision - p);
 
-            number.scale = i;
-            number.kind = NumberBufferKind.Integer;
+            number.Scale = i;
 
             char* dst = number.GetDigitsPointer();
             while (--i >= 0)
@@ -1456,117 +1481,117 @@ namespace System
             // Default empty format to be "G"; custom format is signified with '\0'.
             digits = -1;
             return format.Length == 0 || c == '\0' ? // For compat, treat '\0' as the end of the specifier, even if the specifier extends beyond it.
-                'G' : 
+                'G' :
                 '\0';
         }
 
         internal static unsafe void NumberToString(ref ValueStringBuilder sb, ref NumberBuffer number, char format, int nMaxDigits, NumberFormatInfo info)
         {
-            Debug.Assert(number.kind != NumberBufferKind.Unknown);
+            Debug.Assert(number.Kind != NumberBufferKind.Unknown);
 
             switch (format)
             {
                 case 'C':
                 case 'c':
-                    {
-                        if (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.
+                    RoundNumber(ref number, number.Scale + nMaxDigits); // Don't change this line to use digPos since digCount could have its sign changed.
 
-                        FormatCurrency(ref sb, ref number, nMaxDigits, info);
+                    FormatCurrency(ref sb, ref number, nMaxDigits, info);
 
-                        break;
-                    }
+                    break;
+                }
 
                 case 'F':
                 case 'f':
-                    {
-                        if (nMaxDigits < 0)
-                            nMaxDigits = info.NumberDecimalDigits;
+                {
+                    if (nMaxDigits < 0)
+                        nMaxDigits = info.NumberDecimalDigits;
 
-                        RoundNumber(ref number, number.scale + nMaxDigits);
+                    RoundNumber(ref number, number.Scale + nMaxDigits);
 
-                        if (number.sign)
-                            sb.Append(info.NegativeSign);
+                    if (number.Sign)
+                        sb.Append(info.NegativeSign);
 
-                        FormatFixed(ref sb, ref number, nMaxDigits, info, null, info.NumberDecimalSeparator, null);
+                    FormatFixed(ref sb, ref number, nMaxDigits, info, null, info.NumberDecimalSeparator, null);
 
-                        break;
-                    }
+                    break;
+                }
 
                 case 'N':
                 case 'n':
-                    {
-                        if (nMaxDigits < 0)
-                            nMaxDigits = info.NumberDecimalDigits; // Since we are using digits in our calculation
+                {
+                    if (nMaxDigits < 0)
+                        nMaxDigits = info.NumberDecimalDigits; // Since we are using digits in our calculation
 
-                        RoundNumber(ref number, number.scale + nMaxDigits);
+                    RoundNumber(ref number, number.Scale + nMaxDigits);
 
-                        FormatNumber(ref sb, ref number, nMaxDigits, info);
+                    FormatNumber(ref sb, ref number, nMaxDigits, info);
 
-                        break;
-                    }
+                    break;
+                }
 
                 case 'E':
                 case 'e':
-                    {
-                        if (nMaxDigits < 0)
-                            nMaxDigits = 6;
-                        nMaxDigits++;
+                {
+                    if (nMaxDigits < 0)
+                        nMaxDigits = 6;
+                    nMaxDigits++;
 
-                        RoundNumber(ref number, nMaxDigits);
+                    RoundNumber(ref number, nMaxDigits);
 
-                        if (number.sign)
-                            sb.Append(info.NegativeSign);
+                    if (number.Sign)
+                        sb.Append(info.NegativeSign);
 
-                        FormatScientific(ref sb, ref number, nMaxDigits, info, format);
+                    FormatScientific(ref sb, ref number, nMaxDigits, info, format);
 
-                        break;
-                    }
+                    break;
+                }
 
                 case 'G':
                 case 'g':
+                {
+                    bool noRounding = false;
+                    if (nMaxDigits < 1)
                     {
-                        bool noRounding = false;
-                        if (nMaxDigits < 1)
+                        if ((number.Kind == NumberBufferKind.Decimal) && (nMaxDigits == -1))
                         {
-                            if ((number.kind == NumberBufferKind.Decimal) && (nMaxDigits == -1))
-                            {
-                                noRounding = true;  // Turn off rounding for ECMA compliance to output trailing 0's after decimal as significant
-                                goto SkipRounding;
-                            }
-                            else
-                            {
-                                // This ensures that the PAL code pads out to the correct place even when we use the default precision
-                                nMaxDigits = number.precision;
-                            }
+                            noRounding = true;  // Turn off rounding for ECMA compliance to output trailing 0's after decimal as significant
+                            goto SkipRounding;
                         }
+                        else
+                        {
+                            // This ensures that the PAL code pads out to the correct place even when we use the default precision
+                            nMaxDigits = number.Precision;
+                        }
+                    }
 
-                        RoundNumber(ref number, nMaxDigits);
+                    RoundNumber(ref number, nMaxDigits);
 
-SkipRounding:
-                        if (number.sign)
-                            sb.Append(info.NegativeSign);
+                SkipRounding:
+                    if (number.Sign)
+                        sb.Append(info.NegativeSign);
 
-                        FormatGeneral(ref sb, ref number, nMaxDigits, info, (char)(format - ('G' - 'E')), noRounding);
+                    FormatGeneral(ref sb, ref number, nMaxDigits, info, (char)(format - ('G' - 'E')), noRounding);
 
-                        break;
-                    }
+                    break;
+                }
 
                 case 'P':
                 case 'p':
-                    {
-                        if (nMaxDigits < 0)
-                            nMaxDigits = info.PercentDecimalDigits;
-                        number.scale += 2;
+                {
+                    if (nMaxDigits < 0)
+                        nMaxDigits = info.PercentDecimalDigits;
+                    number.Scale += 2;
 
-                        RoundNumber(ref number, number.scale + nMaxDigits);
+                    RoundNumber(ref number, number.Scale + nMaxDigits);
 
-                        FormatPercent(ref sb, ref number, nMaxDigits, info);
+                    FormatPercent(ref sb, ref number, nMaxDigits, info);
 
-                        break;
-                    }
+                    break;
+                }
 
                 default:
                     throw new FormatException(SR.Argument_BadFormatSpecifier);
@@ -1575,7 +1600,7 @@ SkipRounding:
 
         internal static unsafe void NumberToStringFormat(ref ValueStringBuilder sb, ref NumberBuffer number, ReadOnlySpan<char> format, NumberFormatInfo info)
         {
-            Debug.Assert(number.kind != NumberBufferKind.Unknown);
+            Debug.Assert(number.Kind != NumberBufferKind.Unknown);
 
             int digitCount;
             int decimalPos;
@@ -1594,7 +1619,7 @@ SkipRounding:
             char* dig = number.GetDigitsPointer();
             char ch;
 
-            section = FindSection(format, dig[0] == 0 ? 2 : number.sign ? 1 : 0);
+            section = FindSection(format, dig[0] == 0 ? 2 : number.Sign ? 1 : 0);
 
             while (true)
             {
@@ -1663,7 +1688,8 @@ SkipRounding:
                                 if ((src < format.Length && pFormat[src] == '0') ||
                                     (src + 1 < format.Length && (pFormat[src] == '+' || pFormat[src] == '-') && pFormat[src + 1] == '0'))
                                 {
-                                    while (++src < format.Length && pFormat[src] == '0');
+                                    while (++src < format.Length && pFormat[src] == '0')
+                                        ;
                                     scientific = true;
                                 }
                                 break;
@@ -1684,8 +1710,8 @@ SkipRounding:
 
                 if (dig[0] != 0)
                 {
-                    number.scale += scaleAdjust;
-                    int pos = scientific ? digitCount : number.scale + digitCount - decimalPos;
+                    number.Scale += scaleAdjust;
+                    int pos = scientific ? digitCount : number.Scale + digitCount - decimalPos;
                     RoundNumber(ref number, pos);
                     if (dig[0] == 0)
                     {
@@ -1699,7 +1725,7 @@ SkipRounding:
                 }
                 else
                 {
-                    number.scale = 0;      // Decimals with scale ('0.00') should be rounded.
+                    number.Scale = 0;      // Decimals with scale ('0.00') should be rounded.
                 }
 
                 break;
@@ -1714,8 +1740,8 @@ SkipRounding:
             }
             else
             {
-                digPos = number.scale > decimalPos ? number.scale : decimalPos;
-                adjust = number.scale - decimalPos;
+                digPos = number.Scale > decimalPos ? number.Scale : decimalPos;
+                adjust = number.Scale - decimalPos;
             }
             src = section;
 
@@ -1771,7 +1797,7 @@ SkipRounding:
                 }
             }
 
-            if (number.sign && (section == 0) && (number.scale != 0))
+            if (number.Sign && (section == 0) && (number.Scale != 0))
                 sb.Append(info.NegativeSign);
 
             bool decimalWritten = false;
@@ -1813,47 +1839,47 @@ SkipRounding:
                     {
                         case '#':
                         case '0':
+                        {
+                            if (adjust < 0)
                             {
-                                if (adjust < 0)
-                                {
-                                    adjust++;
-                                    ch = digPos <= firstDigit ? '0' : '\0';
-                                }
-                                else
-                                {
-                                    ch = *cur != 0 ? *cur++ : digPos > lastDigit ? '0' : '\0';
-                                }
-                                if (ch != 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)
                                 {
-                                    sb.Append(ch);
-                                    if (thousandSeps && digPos > 1 && thousandsSepCtr >= 0)
+                                    if (digPos == thousandsSepPos[thousandsSepCtr] + 1)
                                     {
-                                        if (digPos == thousandsSepPos[thousandsSepCtr] + 1)
-                                        {
-                                            sb.Append(info.NumberGroupSeparator);
-                                            thousandsSepCtr--;
-                                        }
+                                        sb.Append(info.NumberGroupSeparator);
+                                        thousandsSepCtr--;
                                     }
                                 }
-
-                                digPos--;
-                                break;
                             }
+
+                            digPos--;
+                            break;
+                        }
                         case '.':
+                        {
+                            if (digPos != 0 || decimalWritten)
                             {
-                                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;
-                                }
+                                // 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;
@@ -1875,54 +1901,54 @@ SkipRounding:
                             break;
                         case 'E':
                         case 'e':
+                        {
+                            bool positiveSign = false;
+                            int i = 0;
+                            if (scientific)
                             {
-                                bool positiveSign = false;
-                                int i = 0;
-                                if (scientific)
+                                if (src < format.Length && pFormat[src] == '0')
                                 {
-                                    if (src < format.Length && pFormat[src] == '0')
-                                    {
-                                        // Handles E0, which should format the same as E-0
-                                        i++;
-                                    }
-                                    else if (src+1 < format.Length && pFormat[src] == '+' && pFormat[src + 1] == '0')
-                                    {
-                                        // Handles E+0
-                                        positiveSign = true;
-                                    }
-                                    else if (src+1 < format.Length && 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 (++src < format.Length && pFormat[src] == '0')
-                                        i++;
-                                    if (i > 10)
-                                        i = 10;
-
-                                    int exp = dig[0] == 0 ? 0 : number.scale - decimalPos;
-                                    FormatExponent(ref sb, info, exp, ch, i, positiveSign);
-                                    scientific = false;
+                                    // Handles E0, which should format the same as E-0
+                                    i++;
+                                }
+                                else if (src + 1 < format.Length && pFormat[src] == '+' && pFormat[src + 1] == '0')
+                                {
+                                    // Handles E+0
+                                    positiveSign = true;
+                                }
+                                else if (src + 1 < format.Length && 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); // Copy E or e to output
-                                    if (src < format.Length)
-                                    {
-                                        if (pFormat[src] == '+' || pFormat[src] == '-')
-                                            sb.Append(pFormat[src++]);
-                                        while (src < format.Length && pFormat[src] == '0')
-                                            sb.Append(pFormat[src++]);
-                                    }
+                                    sb.Append(ch);
+                                    break;
+                                }
+
+                                while (++src < format.Length && pFormat[src] == '0')
+                                    i++;
+                                if (i > 10)
+                                    i = 10;
+
+                                int exp = dig[0] == 0 ? 0 : number.Scale - decimalPos;
+                                FormatExponent(ref sb, info, exp, ch, i, positiveSign);
+                                scientific = false;
+                            }
+                            else
+                            {
+                                sb.Append(ch); // Copy E or e to output
+                                if (src < format.Length)
+                                {
+                                    if (pFormat[src] == '+' || pFormat[src] == '-')
+                                        sb.Append(pFormat[src++]);
+                                    while (src < format.Length && pFormat[src] == '0')
+                                        sb.Append(pFormat[src++]);
                                 }
-                                break;
                             }
+                            break;
+                        }
                         default:
                             sb.Append(ch);
                             break;
@@ -1930,13 +1956,13 @@ SkipRounding:
                 }
             }
 
-            if (number.sign && (section == 0) && (number.scale == 0) && (sb.Length > 0))
+            if (number.Sign && (section == 0) && (number.Scale == 0) && (sb.Length > 0))
                 sb.Insert(0, info.NegativeSign);
         }
 
         private static void FormatCurrency(ref ValueStringBuilder sb, ref NumberBuffer number, int nMaxDigits, NumberFormatInfo info)
         {
-            string fmt = number.sign ?
+            string fmt = number.Sign ?
                 s_negCurrencyFormats[info.CurrencyNegativePattern] :
                 s_posCurrencyFormats[info.CurrencyPositivePattern];
 
@@ -1962,7 +1988,7 @@ SkipRounding:
 
         private static unsafe void FormatFixed(ref ValueStringBuilder sb, ref NumberBuffer number, int nMaxDigits, NumberFormatInfo info, int[] groupDigits, string sDecimal, string sGroup)
         {
-            int digPos = number.scale;
+            int digPos = number.Scale;
             char* dig = number.GetDigitsPointer();
 
             if (digPos > 0)
@@ -2064,7 +2090,7 @@ SkipRounding:
 
         private static void FormatNumber(ref ValueStringBuilder sb, ref NumberBuffer number, int nMaxDigits, NumberFormatInfo info)
         {
-            string fmt = number.sign ?
+            string fmt = number.Sign ?
                 s_negNumberFormats[info.NumberNegativePattern] :
                 PosNumberFormat;
 
@@ -2097,7 +2123,7 @@ SkipRounding:
             while (--nMaxDigits > 0)
                 sb.Append((*dig != 0) ? *dig++ : '0');
 
-            int e = number.digits[0] == 0 ? 0 : number.scale - 1;
+            int e = number.Digits[0] == 0 ? 0 : number.Scale - 1;
             FormatExponent(ref sb, info, e, expChar, 3, true);
         }
 
@@ -2124,7 +2150,7 @@ SkipRounding:
 
         private static unsafe void FormatGeneral(ref ValueStringBuilder sb, ref NumberBuffer number, int nMaxDigits, NumberFormatInfo info, char expChar, bool bSuppressScientific)
         {
-            int digPos = number.scale;
+            int digPos = number.Scale;
             bool scientific = false;
 
             if (!bSuppressScientific)
@@ -2166,12 +2192,12 @@ SkipRounding:
             }
 
             if (scientific)
-                FormatExponent(ref sb, info, number.scale - 1, expChar, 2, true);
+                FormatExponent(ref sb, info, number.Scale - 1, expChar, 2, true);
         }
 
         private static void FormatPercent(ref ValueStringBuilder sb, ref NumberBuffer number, int nMaxDigits, NumberFormatInfo info)
         {
-            string fmt = number.sign ?
+            string fmt = number.Sign ?
                 s_negPercentFormats[info.PercentNegativePattern] :
                 s_posPercentFormats[info.PercentPositivePattern];
 
@@ -2214,7 +2240,7 @@ SkipRounding:
                 }
                 else
                 {
-                    number.scale++;
+                    number.Scale++;
                     dig[0] = '1';
                     i = 1;
                 }
@@ -2226,11 +2252,11 @@ SkipRounding:
             }
             if (i == 0)
             {
-                number.scale = 0;
+                number.Scale = 0;
 
-                if (number.kind == NumberBufferKind.Integer)
+                if (number.Kind == NumberBufferKind.Integer)
                 {
-                    number.sign = false;
+                    number.Sign = false;
                 }
             }
             dig[i] = '\0';
@@ -2247,7 +2273,7 @@ SkipRounding:
             fixed (char* pFormat = &MemoryMarshal.GetReference(format))
             {
                 src = 0;
-                for (;;)
+                for (; ; )
                 {
                     if (src >= format.Length)
                     {
@@ -2291,19 +2317,19 @@ SkipRounding:
 
         private static unsafe void DoubleToNumber(double value, int precision, ref NumberBuffer number)
         {
-            number.precision = precision;
+            number.Precision = precision;
 
             if (!double.IsFinite(value))
             {
-                number.scale = double.IsNaN(value) ? ScaleNAN : ScaleINF;
-                number.sign = double.IsNegative(value);
-                number.digits[0] = '\0';
+                number.Scale = double.IsNaN(value) ? ScaleNAN : ScaleINF;
+                number.Sign = double.IsNegative(value);
+                number.Digits[0] = '\0';
             }
             else if (value == 0.0)
             {
-                number.scale = 0;
-                number.sign = double.IsNegative(value);
-                number.digits[0] = '\0';
+                number.Scale = 0;
+                number.Sign = double.IsNegative(value);
+                number.Digits[0] = '\0';
             }
             else if (!Grisu3.Run(value, precision, ref number))
             {
index 60d629a..0cbf258 100644 (file)
@@ -341,11 +341,11 @@ namespace System
                 if (double.IsNegative(value))
                 {
                     value = -value;
-                    number.sign = true;
+                    number.Sign = true;
                 }
                 else
                 {
-                    number.sign = false;
+                    number.Sign = false;
                 }
 
                 // Step 1: Determine the normalized DiyFp w.
@@ -370,8 +370,8 @@ namespace System
 
                 if (isSuccess)
                 {
-                    number.digits[precision] = '\0';
-                    number.scale = (length - decimalExponent + kappa);
+                    number.Digits[precision] = '\0';
+                    number.Scale = (length - decimalExponent + kappa);
                 }
 
                 return isSuccess;
index b5ed19b..6636383 100644 (file)
@@ -9,26 +9,37 @@ namespace System
 {
     internal static partial class Number
     {
-        private const int NumberMaxDigits = 50;
-
-        private const double Log10V2 = 0.30102999566398119521373889472449;
-
-        // DriftFactor = 1 - Log10V2 - epsilon (a small number account for drift of floating point multiplication)
-        private const double DriftFactor = 0.69;
+        //  We need 1 additional byte, per length, for the terminating null
+        private const int DecimalNumberBufferLength = 50 + 1;
+        private const int DoubleNumberBufferLength = 768 + 1;  // 767 for the longest input + 1 for rounding: 4.9406564584124654E-324 
+        private const int Int32NumberBufferLength = 10 + 1;    // 10 for the longest input: 2,147,483,647
+        private const int Int64NumberBufferLength = 19 + 1;    // 19 for the longest input: 9,223,372,036,854,775,807
+        private const int SingleNumberBufferLength = 113 + 1;  // 112 for the longest input + 1 for rounding: 1.40129846E-45
+        private const int UInt32NumberBufferLength = 10 + 1;   // 10 for the longest input: 4,294,967,295
+        private const int UInt64NumberBufferLength = 20 + 1;   // 20 for the longest input: 18,446,744,073,709,551,615
 
         [StructLayout(LayoutKind.Sequential, Pack = 1)]
         internal unsafe ref struct NumberBuffer
         {
-            public int precision;
-            public int scale;
-            public bool sign;
-            public NumberBufferKind kind;
-            public fixed char digits[NumberMaxDigits + 1];
+            public int Precision;
+            public int Scale;
+            public bool Sign;
+            public NumberBufferKind Kind;
+            public Span<char> Digits;
+
+            public NumberBuffer(NumberBufferKind kind, char* pDigits, int digitsLength)
+            {
+                Precision = 0;
+                Scale = 0;
+                Sign = false;
+                Kind = kind;
+                Digits = new Span<char>(pDigits, digitsLength);
+            }
 
             public char* GetDigitsPointer()
             {
                 // This is safe to do since we are a ref struct
-                return (char*)(Unsafe.AsPointer(ref digits[0]));
+                return (char*)(Unsafe.AsPointer(ref Digits[0]));
             }
         }
 
@@ -37,7 +48,7 @@ namespace System
             Unknown = 0,
             Integer = 1,
             Decimal = 2,
-            Double = 3
+            Double = 3,
         }
     }
 }
index 2df2b2a..974786d 100644 (file)
@@ -327,7 +327,7 @@ namespace System
 
             if (remaining == 0)
             {
-                return number.sign ? -0.0 : 0.0;
+                return number.Sign ? -0.0 : 0.0;
             }
 
             int count = Math.Min(remaining, 9);
@@ -344,18 +344,18 @@ namespace System
                 val = Mul32x32To64((uint)(val), mult) + DigitsToInt(src + 9, count);
             }
 
-            int scale = number.scale - (total - remaining);
+            int scale = number.Scale - (total - remaining);
             int absscale = Math.Abs(scale);
             if (absscale >= 22 * 16)
             {
                 // overflow / underflow
                 if (scale > 0)
                 {
-                    return number.sign ? double.NegativeInfinity : double.PositiveInfinity;
+                    return number.Sign ? double.NegativeInfinity : double.PositiveInfinity;
                 }
                 else
                 {
-                    return number.sign ? -0.0 : 0.0;
+                    return number.Sign ? -0.0 : 0.0;
                 }
             }
 
@@ -438,7 +438,7 @@ namespace System
                 val = ((ulong)(exp) << 52) + ((val >> 11) & 0x000FFFFFFFFFFFFF);
             }
 
-            if (number.sign)
+            if (number.Sign)
             {
                 val |= 0x8000000000000000;
             }
index 2f25e76..802f157 100644 (file)
@@ -52,10 +52,10 @@ namespace System
             0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF  // 255
         };
 
-        private static unsafe bool NumberToInt32(ref NumberBuffer number, ref int value)
+        private static unsafe bool TryNumberToInt32(ref NumberBuffer number, ref int value)
         {
-            int i = number.scale;
-            if (i > Int32Precision || i < number.precision)
+            int i = number.Scale;
+            if (i > Int32Precision || i < number.Precision)
             {
                 return false;
             }
@@ -74,7 +74,7 @@ namespace System
                     n += (*p++ - '0');
                 }
             }
-            if (number.sign)
+            if (number.Sign)
             {
                 n = -n;
                 if (n > 0)
@@ -93,10 +93,10 @@ namespace System
             return true;
         }
 
-        private static unsafe bool NumberToInt64(ref NumberBuffer number, ref long value)
+        private static unsafe bool TryNumberToInt64(ref NumberBuffer number, ref long value)
         {
-            int i = number.scale;
-            if (i > Int64Precision || i < number.precision)
+            int i = number.Scale;
+            if (i > Int64Precision || i < number.Precision)
             {
                 return false;
             }
@@ -115,7 +115,7 @@ namespace System
                     n += (*p++ - '0');
                 }
             }
-            if (number.sign)
+            if (number.Sign)
             {
                 n = -n;
                 if (n > 0)
@@ -134,10 +134,10 @@ namespace System
             return true;
         }
 
-        private static unsafe bool NumberToUInt32(ref NumberBuffer number, ref uint value)
+        private static unsafe bool TryNumberToUInt32(ref NumberBuffer number, ref uint value)
         {
-            int i = number.scale;
-            if (i > UInt32Precision || i < number.precision || number.sign)
+            int i = number.Scale;
+            if (i > UInt32Precision || i < number.Precision || number.Sign)
             {
                 return false;
             }
@@ -166,10 +166,10 @@ namespace System
             return true;
         }
 
-        private static unsafe bool NumberToUInt64(ref NumberBuffer number, ref ulong value)
+        private static unsafe bool TryNumberToUInt64(ref NumberBuffer number, ref ulong value)
         {
-            int i = number.scale;
-            if (i > UInt64Precision || i < number.precision || number.sign)
+            int i = number.Scale;
+            if (i > UInt64Precision || i < number.Precision || number.Sign)
             {
                 return false;
             }
@@ -200,139 +200,45 @@ namespace System
 
         internal static int ParseInt32(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
         {
-            if ((styles & ~NumberStyles.Integer) == 0)
+            if (!TryParseInt32(value, styles, info, out int result, out bool failureIsOverflow))
             {
-                // Optimized path for the common case of anything that's allowed for integer style.
-                bool overflow = false;
-                if (!TryParseInt32IntegerStyle(value, styles, info, out int intResult, ref overflow))
-                {
-                    ThrowOverflowOrFormatException(overflow, nameof(SR.Overflow_Int32));
-                }
-                return intResult;
+                ThrowOverflowOrFormatException(failureIsOverflow, nameof(SR.Overflow_Int32));
             }
 
-            if ((styles & NumberStyles.AllowHexSpecifier) != 0)
-            {
-                bool overflow = false;
-                if (!TryParseUInt32HexNumberStyle(value, styles, out uint hexResult, ref overflow))
-                {
-                    ThrowOverflowOrFormatException(overflow, nameof(SR.Overflow_Int32));
-                }
-                return (int)hexResult;
-            }
-
-            NumberBuffer number = default;
-            int result = 0;
-            StringToNumber(value, styles, ref number, info, false);
-            if (!NumberToInt32(ref number, ref result))
-            {
-                ThrowOverflowOrFormatException(overflow: true, nameof(SR.Overflow_Int32));
-            }
             return result;
         }
 
         internal static long ParseInt64(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
         {
-            if ((styles & ~NumberStyles.Integer) == 0)
-            {
-                // Optimized path for the common case of anything that's allowed for integer style.
-                bool overflow = false;
-                if (!TryParseInt64IntegerStyle(value, styles, info, out long intResult, ref overflow))
-                {
-                    ThrowOverflowOrFormatException(overflow, nameof(SR.Overflow_Int64));
-                }
-                return intResult;
-            }
-
-            if ((styles & NumberStyles.AllowHexSpecifier) != 0)
+            if (!TryParseInt64(value, styles, info, out long result, out bool failureIsOverflow))
             {
-                bool overflow = false;
-                if (!TryParseUInt64HexNumberStyle(value, styles, out ulong hexResult, ref overflow))
-                {
-                    ThrowOverflowOrFormatException(overflow, nameof(SR.Overflow_Int64));
-                }
-                return (long)hexResult;
+                ThrowOverflowOrFormatException(failureIsOverflow, nameof(SR.Overflow_Int64));
             }
 
-            NumberBuffer number = default;
-            long result = 0;
-            StringToNumber(value, styles, ref number, info, false);
-            if (!NumberToInt64(ref number, ref result))
-            {
-                ThrowOverflowOrFormatException(overflow: true, nameof(SR.Overflow_Int64));
-            }
             return result;
         }
 
         internal static uint ParseUInt32(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
         {
-            uint result = 0;
-
-            if ((styles & ~NumberStyles.Integer) == 0)
-            {
-                // Optimized path for the common case of anything that's allowed for integer style.
-                bool overflow = false;
-                if (!TryParseUInt32IntegerStyle(value, styles, info, out result, ref overflow))
-                {
-                    ThrowOverflowOrFormatException(overflow, nameof(SR.Overflow_UInt32));
-                }
-                return result;
-            }
-
-            if ((styles & NumberStyles.AllowHexSpecifier) != 0)
+            if (!TryParseUInt32(value, styles, info, out uint result, out bool failureIsOverflow))
             {
-                bool overflow = false;
-                if (!TryParseUInt32HexNumberStyle(value, styles, out result, ref overflow))
-                {
-                    ThrowOverflowOrFormatException(overflow, nameof(SR.Overflow_UInt32));
-                }
-                return result;
+                ThrowOverflowOrFormatException(failureIsOverflow, nameof(SR.Overflow_UInt32));
             }
 
-            NumberBuffer number = default;
-            StringToNumber(value, styles, ref number, info, false);
-            if (!NumberToUInt32(ref number, ref result))
-            {
-                ThrowOverflowOrFormatException(overflow: true, nameof(SR.Overflow_UInt32));
-            }
             return result;
         }
 
         internal static ulong ParseUInt64(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
         {
-            ulong result = 0;
-
-            if ((styles & ~NumberStyles.Integer) == 0)
-            {
-                // Optimized path for the common case of anything that's allowed for integer style.
-                bool overflow = false;
-                if (!TryParseUInt64IntegerStyle(value, styles, info, out result, ref overflow))
-                {
-                    ThrowOverflowOrFormatException(overflow, nameof(SR.Overflow_UInt64));
-                }
-                return result;
-            }
-
-            if ((styles & NumberStyles.AllowHexSpecifier) != 0)
+            if (!TryParseUInt64(value, styles, info, out ulong result, out bool failureIsOverflow))
             {
-                bool overflow = false;
-                if (!TryParseUInt64HexNumberStyle(value, styles, out result, ref overflow))
-                {
-                    ThrowOverflowOrFormatException(overflow, nameof(SR.Overflow_UInt64));
-                }
-                return result;
+                ThrowOverflowOrFormatException(failureIsOverflow, nameof(SR.Overflow_UInt64));
             }
 
-            NumberBuffer number = default;
-            StringToNumber(value, styles, ref number, info, false);
-            if (!NumberToUInt64(ref number, ref result))
-            {
-                ThrowOverflowOrFormatException(overflow: true, nameof(SR.Overflow_UInt64));
-            }
             return result;
         }
 
-        private static unsafe bool ParseNumber(ref char* str, char* strEnd, NumberStyles styles, ref NumberBuffer number, NumberFormatInfo info, bool parseDecimal)
+        private static unsafe bool TryParseNumber(ref char* str, char* strEnd, NumberStyles styles, ref NumberBuffer number, NumberFormatInfo info)
         {
             Debug.Assert(str != null);
             Debug.Assert(strEnd != null);
@@ -346,8 +252,8 @@ namespace System
             const int StateDecimal = 0x0010;
             const int StateCurrency = 0x0020;
 
-            number.scale = 0;
-            number.sign = false;
+            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.
@@ -380,7 +286,7 @@ namespace System
                 // "-Kr 1231.47" is legal but "- 1231.47" is not.
                 if (!IsWhite(ch) || (styles & NumberStyles.AllowLeadingWhite) == 0 || ((state & StateSign) != 0 && ((state & StateCurrency) == 0 && info.NumberNegativePattern != 2)))
                 {
-                    if ((((styles & NumberStyles.AllowLeadingSign) != 0) && (state & StateSign) == 0) && ((next = MatchChars(p, strEnd, info.PositiveSign)) != null || ((next = MatchChars(p, strEnd, info.NegativeSign)) != null && (number.sign = true))))
+                    if ((((styles & NumberStyles.AllowLeadingSign) != 0) && (state & StateSign) == 0) && ((next = MatchChars(p, strEnd, info.PositiveSign)) != null || ((next = MatchChars(p, strEnd, info.NegativeSign)) != null && (number.Sign = true))))
                     {
                         state |= StateSign;
                         p = next - 1;
@@ -388,7 +294,7 @@ namespace System
                     else if (ch == '(' && ((styles & NumberStyles.AllowParentheses) != 0) && ((state & StateSign) == 0))
                     {
                         state |= StateSign | StateParens;
-                        number.sign = true;
+                        number.Sign = true;
                     }
                     else if (currSymbol != null && (next = MatchChars(p, strEnd, currSymbol)) != null)
                     {
@@ -405,8 +311,11 @@ namespace System
                 }
                 ch = ++p < strEnd ? *p : '\0';
             }
+
             int digCount = 0;
             int digEnd = 0;
+            int maxDigCount = number.Digits.Length - 1;
+
             while (true)
             {
                 if (IsDigit(ch))
@@ -415,23 +324,23 @@ namespace System
 
                     if (ch != '0' || (state & StateNonZero) != 0)
                     {
-                        if (digCount < NumberMaxDigits)
+                        if (digCount < maxDigCount)
                         {
-                            number.digits[digCount++] = ch;
-                            if (ch != '0' || parseDecimal)
+                            number.Digits[digCount++] = ch;
+                            if (ch != '0' || number.Kind == NumberBufferKind.Decimal)
                             {
                                 digEnd = digCount;
                             }
                         }
                         if ((state & StateDecimal) == 0)
                         {
-                            number.scale++;
+                            number.Scale++;
                         }
                         state |= StateNonZero;
                     }
                     else if ((state & StateDecimal) != 0)
                     {
-                        number.scale--;
+                        number.Scale--;
                     }
                 }
                 else if (((styles & NumberStyles.AllowDecimalPoint) != 0) && ((state & StateDecimal) == 0) && ((next = MatchChars(p, strEnd, decSep)) != null || ((parsingCurrency) && (state & StateCurrency) == 0) && (next = MatchChars(p, strEnd, info.NumberDecimalSeparator)) != null))
@@ -451,8 +360,8 @@ namespace System
             }
 
             bool negExp = false;
-            number.precision = digEnd;
-            number.digits[digEnd] = '\0';
+            number.Precision = digEnd;
+            number.Digits[digEnd] = '\0';
             if ((state & StateDigits) != 0)
             {
                 if ((ch == 'E' || ch == 'e') && ((styles & NumberStyles.AllowExponent) != 0))
@@ -488,7 +397,7 @@ namespace System
                         {
                             exp = -exp;
                         }
-                        number.scale += exp;
+                        number.Scale += exp;
                     }
                     else
                     {
@@ -500,7 +409,7 @@ namespace System
                 {
                     if (!IsWhite(ch) || (styles & NumberStyles.AllowTrailingWhite) == 0)
                     {
-                        if (((styles & NumberStyles.AllowTrailingSign) != 0 && ((state & StateSign) == 0)) && ((next = MatchChars(p, strEnd, info.PositiveSign)) != null || (((next = MatchChars(p, strEnd, info.NegativeSign)) != null) && (number.sign = true))))
+                        if (((styles & NumberStyles.AllowTrailingSign) != 0 && ((state & StateSign) == 0)) && ((next = MatchChars(p, strEnd, info.PositiveSign)) != null || (((next = MatchChars(p, strEnd, info.NegativeSign)) != null) && (number.Sign = true))))
                         {
                             state |= StateSign;
                             p = next - 1;
@@ -525,13 +434,13 @@ namespace System
                 {
                     if ((state & StateNonZero) == 0)
                     {
-                        if (!parseDecimal)
+                        if (number.Kind != NumberBufferKind.Decimal)
                         {
-                            number.scale = 0;
+                            number.Scale = 0;
                         }
                         if ((state & StateDecimal) == 0)
                         {
-                            number.sign = false;
+                            number.Sign = false;
                         }
                     }
                     str = p;
@@ -542,27 +451,37 @@ namespace System
             return false;
         }
 
-        internal static bool TryParseInt32(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out int result)
+        internal static unsafe bool TryParseInt32(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out int result, out bool failureIsOverflow)
         {
+            result = 0;
+            failureIsOverflow = false;
+
             if ((styles & ~NumberStyles.Integer) == 0)
             {
                 // Optimized path for the common case of anything that's allowed for integer style.
-                bool overflow = false;
-                return TryParseInt32IntegerStyle(value, styles, info, out result, ref overflow);
+                return TryParseInt32IntegerStyle(value, styles, info, out result, ref failureIsOverflow);
             }
 
-            result = 0;
-
             if ((styles & NumberStyles.AllowHexSpecifier) != 0)
             {
-                bool overflow = false;
-                return TryParseUInt32HexNumberStyle(value, styles, out Unsafe.As<int, uint>(ref result), ref overflow);
+                return TryParseUInt32HexNumberStyle(value, styles, out Unsafe.As<int, uint>(ref result), ref failureIsOverflow);
+            }
+
+            char* pDigits = stackalloc char[Int32NumberBufferLength];
+            NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, Int32NumberBufferLength);
+
+            if (!TryStringToNumber(value, styles, ref number, info))
+            {
+                return false;
+            }
+
+            if (!TryNumberToInt32(ref number, ref result))
+            {
+                failureIsOverflow = true;
+                return false;
             }
 
-            NumberBuffer number = default;
-            return
-                TryStringToNumber(value, styles, ref number, info, false) &&
-                NumberToInt32(ref number, ref result);
+            return true;
         }
 
         /// <summary>Parses int limited to styles that make up NumberStyles.Integer.</summary>
@@ -571,7 +490,8 @@ namespace System
             Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format");
             Debug.Assert(!failureIsOverflow, $"failureIsOverflow should have been initialized to false");
 
-            if ((uint)value.Length < 1) goto FalseExit;
+            if ((uint)value.Length < 1)
+                goto FalseExit;
 
             bool overflow = false;
             int sign = 1;
@@ -584,7 +504,8 @@ namespace System
                 do
                 {
                     index++;
-                    if ((uint)index >= (uint)value.Length) goto FalseExit;
+                    if ((uint)index >= (uint)value.Length)
+                        goto FalseExit;
                     num = value[index];
                 }
                 while (IsWhite(num));
@@ -601,13 +522,15 @@ namespace System
                     {
                         sign = -1;
                         index++;
-                        if ((uint)index >= (uint)value.Length) goto FalseExit;
+                        if ((uint)index >= (uint)value.Length)
+                            goto FalseExit;
                         num = value[index];
                     }
                     else if (num == '+')
                     {
                         index++;
-                        if ((uint)index >= (uint)value.Length) goto FalseExit;
+                        if ((uint)index >= (uint)value.Length)
+                            goto FalseExit;
                         num = value[index];
                     }
                 }
@@ -618,19 +541,21 @@ namespace System
                     if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign))
                     {
                         index += positiveSign.Length;
-                        if ((uint)index >= (uint)value.Length) goto FalseExit;
+                        if ((uint)index >= (uint)value.Length)
+                            goto FalseExit;
                         num = value[index];
                     }
                     else if (!string.IsNullOrEmpty(negativeSign) && value.StartsWith(negativeSign))
                     {
                         sign = -1;
                         index += negativeSign.Length;
-                        if ((uint)index >= (uint)value.Length) goto FalseExit;
+                        if ((uint)index >= (uint)value.Length)
+                            goto FalseExit;
                         num = value[index];
                     }
                 }
             }
+
             int answer = 0;
 
             if (IsDigit(num))
@@ -641,28 +566,34 @@ namespace System
                     do
                     {
                         index++;
-                        if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+                        if ((uint)index >= (uint)value.Length)
+                            goto DoneAtEndButPotentialOverflow;
                         num = value[index];
                     } while (num == '0');
-                    if (!IsDigit(num)) goto HasTrailingChars;
+                    if (!IsDigit(num))
+                        goto HasTrailingChars;
                 }
+
                 // Parse most digits, up to the potential for overflow, which can't happen until after 9 digits.
                 answer = num - '0'; // first digit
                 index++;
                 for (int i = 0; i < 8; i++) // next 8 digits can't overflow
                 {
-                    if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+                    if ((uint)index >= (uint)value.Length)
+                        goto DoneAtEndButPotentialOverflow;
                     num = value[index];
-                    if (!IsDigit(num)) goto HasTrailingChars;
+                    if (!IsDigit(num))
+                        goto HasTrailingChars;
                     index++;
                     answer = 10 * answer + num - '0';
                 }
 
                 // Potential overflow now processing the 10th digit.
-                if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+                if ((uint)index >= (uint)value.Length)
+                    goto DoneAtEndButPotentialOverflow;
                 num = value[index];
-                if (!IsDigit(num)) goto HasTrailingChars;
+                if (!IsDigit(num))
+                    goto HasTrailingChars;
                 index++;
                 if (answer > int.MaxValue / 10)
                 {
@@ -673,7 +604,8 @@ namespace System
                 {
                     overflow = true;
                 }
-                if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+                if ((uint)index >= (uint)value.Length)
+                    goto DoneAtEndButPotentialOverflow;
 
                 // At this point, we're either overflowing or hitting a formatting error.
                 // Format errors take precedence for compatibility.
@@ -688,7 +620,7 @@ namespace System
                 }
                 goto HasTrailingChars;
             }
+
         FalseExit: // parsing failed
             result = 0;
             return false;
@@ -706,16 +638,20 @@ namespace System
             // Skip past trailing whitespace, then past trailing zeros, and if anything else remains, fail.
             if (IsWhite(num))
             {
-                if ((styles & NumberStyles.AllowTrailingWhite) == 0) goto FalseExit;
+                if ((styles & NumberStyles.AllowTrailingWhite) == 0)
+                    goto FalseExit;
                 for (index++; index < value.Length; index++)
                 {
-                    if (!IsWhite(value[index])) break;
+                    if (!IsWhite(value[index]))
+                        break;
                 }
-                if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+                if ((uint)index >= (uint)value.Length)
+                    goto DoneAtEndButPotentialOverflow;
             }
 
-            if (!TrailingZeros(value, index)) goto FalseExit;
-            
+            if (!TrailingZeros(value, index))
+                goto FalseExit;
+
             goto DoneAtEndButPotentialOverflow;
         }
 
@@ -726,7 +662,8 @@ namespace System
             Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format");
             Debug.Assert(!failureIsOverflow, $"failureIsOverflow should have been initialized to false");
 
-            if ((uint)value.Length < 1) goto FalseExit;
+            if ((uint)value.Length < 1)
+                goto FalseExit;
 
             bool overflow = false;
             int sign = 1;
@@ -739,7 +676,8 @@ namespace System
                 do
                 {
                     index++;
-                    if ((uint)index >= (uint)value.Length) goto FalseExit;
+                    if ((uint)index >= (uint)value.Length)
+                        goto FalseExit;
                     num = value[index];
                 }
                 while (IsWhite(num));
@@ -756,13 +694,15 @@ namespace System
                     {
                         sign = -1;
                         index++;
-                        if ((uint)index >= (uint)value.Length) goto FalseExit;
+                        if ((uint)index >= (uint)value.Length)
+                            goto FalseExit;
                         num = value[index];
                     }
                     else if (num == '+')
                     {
                         index++;
-                        if ((uint)index >= (uint)value.Length) goto FalseExit;
+                        if ((uint)index >= (uint)value.Length)
+                            goto FalseExit;
                         num = value[index];
                     }
                 }
@@ -773,19 +713,21 @@ namespace System
                     if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign))
                     {
                         index += positiveSign.Length;
-                        if ((uint)index >= (uint)value.Length) goto FalseExit;
+                        if ((uint)index >= (uint)value.Length)
+                            goto FalseExit;
                         num = value[index];
                     }
                     else if (!string.IsNullOrEmpty(negativeSign) && value.StartsWith(negativeSign))
                     {
                         sign = -1;
                         index += negativeSign.Length;
-                        if ((uint)index >= (uint)value.Length) goto FalseExit;
+                        if ((uint)index >= (uint)value.Length)
+                            goto FalseExit;
                         num = value[index];
                     }
                 }
             }
+
             long answer = 0;
 
             if (IsDigit(num))
@@ -796,28 +738,34 @@ namespace System
                     do
                     {
                         index++;
-                        if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+                        if ((uint)index >= (uint)value.Length)
+                            goto DoneAtEndButPotentialOverflow;
                         num = value[index];
                     } while (num == '0');
-                    if (!IsDigit(num)) goto HasTrailingChars;
+                    if (!IsDigit(num))
+                        goto HasTrailingChars;
                 }
+
                 // Parse most digits, up to the potential for overflow, which can't happen until after 18 digits.
                 answer = num - '0'; // first digit
                 index++;
                 for (int i = 0; i < 17; i++) // next 17 digits can't overflow
                 {
-                    if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+                    if ((uint)index >= (uint)value.Length)
+                        goto DoneAtEndButPotentialOverflow;
                     num = value[index];
-                    if (!IsDigit(num)) goto HasTrailingChars;
+                    if (!IsDigit(num))
+                        goto HasTrailingChars;
                     index++;
                     answer = 10 * answer + num - '0';
                 }
 
                 // Potential overflow now processing the 19th digit.
-                if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+                if ((uint)index >= (uint)value.Length)
+                    goto DoneAtEndButPotentialOverflow;
                 num = value[index];
-                if (!IsDigit(num)) goto HasTrailingChars;
+                if (!IsDigit(num))
+                    goto HasTrailingChars;
                 index++;
                 if (answer > long.MaxValue / 10)
                 {
@@ -828,7 +776,8 @@ namespace System
                 {
                     overflow = true;
                 }
-                if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+                if ((uint)index >= (uint)value.Length)
+                    goto DoneAtEndButPotentialOverflow;
 
                 // At this point, we're either overflowing or hitting a formatting error.
                 // Format errors take precedence for compatibility.
@@ -837,12 +786,13 @@ namespace System
                 {
                     overflow = true;
                     index++;
-                    if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+                    if ((uint)index >= (uint)value.Length)
+                        goto DoneAtEndButPotentialOverflow;
                     num = value[index];
                 }
                 goto HasTrailingChars;
             }
+
         FalseExit: // parsing failed
             result = 0;
             return false;
@@ -860,62 +810,88 @@ namespace System
             // Skip past trailing whitespace, then past trailing zeros, and if anything else remains, fail.
             if (IsWhite(num))
             {
-                if ((styles & NumberStyles.AllowTrailingWhite) == 0) goto FalseExit;
+                if ((styles & NumberStyles.AllowTrailingWhite) == 0)
+                    goto FalseExit;
                 for (index++; index < value.Length; index++)
                 {
-                    if (!IsWhite(value[index])) break;
+                    if (!IsWhite(value[index]))
+                        break;
                 }
-                if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+                if ((uint)index >= (uint)value.Length)
+                    goto DoneAtEndButPotentialOverflow;
             }
 
-            if (!TrailingZeros(value, index)) goto FalseExit;
-            
+            if (!TrailingZeros(value, index))
+                goto FalseExit;
+
             goto DoneAtEndButPotentialOverflow;
         }
 
-        internal static bool TryParseInt64(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out long result)
+        internal static unsafe bool TryParseInt64(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out long result, out bool failureIsOverflow)
         {
+            result = 0;
+            failureIsOverflow = false;
+
             if ((styles & ~NumberStyles.Integer) == 0)
             {
                 // Optimized path for the common case of anything that's allowed for integer style.
-                bool overflow = false;
-                return TryParseInt64IntegerStyle(value, styles, info, out result, ref overflow);
+                return TryParseInt64IntegerStyle(value, styles, info, out result, ref failureIsOverflow);
             }
 
-            result = 0;
-
             if ((styles & NumberStyles.AllowHexSpecifier) != 0)
             {
-                bool overflow = false;
-                return TryParseUInt64HexNumberStyle(value, styles, out Unsafe.As<long, ulong>(ref result), ref overflow);
+                return TryParseUInt64HexNumberStyle(value, styles, out Unsafe.As<long, ulong>(ref result), ref failureIsOverflow);
+            }
+
+            char* pDigits = stackalloc char[Int64NumberBufferLength];
+            NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, Int64NumberBufferLength);
+
+            if (!TryStringToNumber(value, styles, ref number, info))
+            {
+                return false;
             }
 
-            NumberBuffer number = default;
-            return
-                TryStringToNumber(value, styles, ref number, info, false) &&
-                NumberToInt64(ref number, ref result);
+            if (!TryNumberToInt64(ref number, ref result))
+            {
+                failureIsOverflow = true;
+                return false;
+            }
+
+            return true;
         }
 
-        internal static bool TryParseUInt32(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out uint result)
+        internal static unsafe bool TryParseUInt32(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out uint result, out bool failureIsOverflow)
         {
+            result = 0;
+            failureIsOverflow = false;
+
             if ((styles & ~NumberStyles.Integer) == 0)
             {
                 // Optimized path for the common case of anything that's allowed for integer style.
-                bool overflow = false;
-                return TryParseUInt32IntegerStyle(value, styles, info, out result, ref overflow);
+                return TryParseUInt32IntegerStyle(value, styles, info, out result, ref failureIsOverflow);
             }
 
             if ((styles & NumberStyles.AllowHexSpecifier) != 0)
             {
-                bool overflow = false;
-                return TryParseUInt32HexNumberStyle(value, styles, out result, ref overflow);
+                return TryParseUInt32HexNumberStyle(value, styles, out result, ref failureIsOverflow);
             }
 
-            NumberBuffer number = default;
-            result = 0;
-            return
-                TryStringToNumber(value, styles, ref number, info, false) &&
-                NumberToUInt32(ref number, ref result);
+            char* pDigits = stackalloc char[UInt32NumberBufferLength];
+            NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, UInt32NumberBufferLength);
+
+            if (!TryStringToNumber(value, styles, ref number, info))
+            {
+                return false;
+            }
+
+
+            if (!TryNumberToUInt32(ref number, ref result))
+            {
+                failureIsOverflow = true;
+                return false;
+            }
+
+            return true;
         }
 
         /// <summary>Parses uint limited to styles that make up NumberStyles.Integer.</summary>
@@ -925,7 +901,8 @@ namespace System
             Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format");
             Debug.Assert(!failureIsOverflow, $"failureIsOverflow should have been initialized to false");
 
-            if ((uint)value.Length < 1) goto FalseExit;
+            if ((uint)value.Length < 1)
+                goto FalseExit;
 
             bool overflow = false;
             bool hasNegativeSign = false;
@@ -938,7 +915,8 @@ namespace System
                 do
                 {
                     index++;
-                    if ((uint)index >= (uint)value.Length) goto FalseExit;
+                    if ((uint)index >= (uint)value.Length)
+                        goto FalseExit;
                     num = value[index];
                 }
                 while (IsWhite(num));
@@ -954,14 +932,16 @@ namespace System
                     if (num == '+')
                     {
                         index++;
-                        if ((uint)index >= (uint)value.Length) goto FalseExit;
+                        if ((uint)index >= (uint)value.Length)
+                            goto FalseExit;
                         num = value[index];
                     }
                     else if (num == '-')
                     {
                         hasNegativeSign = true;
                         index++;
-                        if ((uint)index >= (uint)value.Length) goto FalseExit;
+                        if ((uint)index >= (uint)value.Length)
+                            goto FalseExit;
                         num = value[index];
                     }
                 }
@@ -972,19 +952,21 @@ namespace System
                     if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign))
                     {
                         index += positiveSign.Length;
-                        if ((uint)index >= (uint)value.Length) goto FalseExit;
+                        if ((uint)index >= (uint)value.Length)
+                            goto FalseExit;
                         num = value[index];
                     }
                     else if (!string.IsNullOrEmpty(negativeSign) && value.StartsWith(negativeSign))
                     {
                         hasNegativeSign = true;
                         index += negativeSign.Length;
-                        if ((uint)index >= (uint)value.Length) goto FalseExit;
+                        if ((uint)index >= (uint)value.Length)
+                            goto FalseExit;
                         num = value[index];
                     }
                 }
             }
+
             int answer = 0;
 
             if (IsDigit(num))
@@ -995,35 +977,42 @@ namespace System
                     do
                     {
                         index++;
-                        if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+                        if ((uint)index >= (uint)value.Length)
+                            goto DoneAtEndButPotentialOverflow;
                         num = value[index];
                     } while (num == '0');
-                    if (!IsDigit(num)) goto HasTrailingChars;
+                    if (!IsDigit(num))
+                        goto HasTrailingChars;
                 }
+
                 // Parse most digits, up to the potential for overflow, which can't happen until after 9 digits.
                 answer = num - '0'; // first digit
                 index++;
                 for (int i = 0; i < 8; i++) // next 8 digits can't overflow
                 {
-                    if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+                    if ((uint)index >= (uint)value.Length)
+                        goto DoneAtEndButPotentialOverflow;
                     num = value[index];
-                    if (!IsDigit(num)) goto HasTrailingChars;
+                    if (!IsDigit(num))
+                        goto HasTrailingChars;
                     index++;
                     answer = 10 * answer + num - '0';
                 }
 
                 // Potential overflow now processing the 10th digit.
-                if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+                if ((uint)index >= (uint)value.Length)
+                    goto DoneAtEndButPotentialOverflow;
                 num = value[index];
-                if (!IsDigit(num)) goto HasTrailingChars;
+                if (!IsDigit(num))
+                    goto HasTrailingChars;
                 index++;
                 if ((uint)answer > uint.MaxValue / 10 || ((uint)answer == uint.MaxValue / 10 && num > '5'))
                 {
                     overflow = true;
                 }
                 answer = answer * 10 + num - '0';
-                if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+                if ((uint)index >= (uint)value.Length)
+                    goto DoneAtEndButPotentialOverflow;
 
                 // At this point, we're either overflowing or hitting a formatting error.
                 // Format errors take precedence for compatibility.
@@ -1032,12 +1021,13 @@ namespace System
                 {
                     overflow = true;
                     index++;
-                    if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+                    if ((uint)index >= (uint)value.Length)
+                        goto DoneAtEndButPotentialOverflow;
                     num = value[index];
                 }
                 goto HasTrailingChars;
             }
+
         FalseExit: // parsing failed
             result = 0;
             return false;
@@ -1055,16 +1045,20 @@ namespace System
             // Skip past trailing whitespace, then past trailing zeros, and if anything else remains, fail.
             if (IsWhite(num))
             {
-                if ((styles & NumberStyles.AllowTrailingWhite) == 0) goto FalseExit;
+                if ((styles & NumberStyles.AllowTrailingWhite) == 0)
+                    goto FalseExit;
                 for (index++; index < value.Length; index++)
                 {
-                    if (!IsWhite(value[index])) break;
+                    if (!IsWhite(value[index]))
+                        break;
                 }
-                if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+                if ((uint)index >= (uint)value.Length)
+                    goto DoneAtEndButPotentialOverflow;
             }
 
-            if (!TrailingZeros(value, index)) goto FalseExit;
-            
+            if (!TrailingZeros(value, index))
+                goto FalseExit;
+
             goto DoneAtEndButPotentialOverflow;
         }
 
@@ -1075,7 +1069,8 @@ namespace System
             Debug.Assert((styles & ~NumberStyles.HexNumber) == 0, "Only handles subsets of HexNumber format");
             Debug.Assert(!failureIsOverflow, $"failureIsOverflow should have been initialized to false");
 
-            if ((uint)value.Length < 1) goto FalseExit;
+            if ((uint)value.Length < 1)
+                goto FalseExit;
 
             bool overflow = false;
             int index = 0;
@@ -1088,7 +1083,8 @@ namespace System
                 do
                 {
                     index++;
-                    if ((uint)index >= (uint)value.Length) goto FalseExit;
+                    if ((uint)index >= (uint)value.Length)
+                        goto FalseExit;
                     num = value[index];
                 }
                 while (IsWhite(num));
@@ -1105,31 +1101,38 @@ namespace System
                     do
                     {
                         index++;
-                        if ((uint)index >= (uint)value.Length) goto DoneAtEnd;
+                        if ((uint)index >= (uint)value.Length)
+                            goto DoneAtEnd;
                         num = value[index];
                     } while (num == '0');
-                    if ((uint)num >= (uint)charToHexLookup.Length || charToHexLookup[num] == 0xFF) goto HasTrailingChars;
+                    if ((uint)num >= (uint)charToHexLookup.Length || charToHexLookup[num] == 0xFF)
+                        goto HasTrailingChars;
                 }
+
                 // Parse up through 8 digits, as no overflow is possible
                 answer = charToHexLookup[num]; // first digit
                 index++;
                 for (int i = 0; i < 7; i++) // next 7 digits can't overflow
                 {
-                    if ((uint)index >= (uint)value.Length) goto DoneAtEnd;
+                    if ((uint)index >= (uint)value.Length)
+                        goto DoneAtEnd;
                     num = value[index];
-                    if ((uint)num >= (uint)charToHexLookup.Length || (numValue = charToHexLookup[num]) == 0xFF) goto HasTrailingChars;
+                    if ((uint)num >= (uint)charToHexLookup.Length || (numValue = charToHexLookup[num]) == 0xFF)
+                        goto HasTrailingChars;
                     index++;
                     answer = 16 * answer + numValue;
                 }
 
                 // If there's another digit, it's an overflow.
-                if ((uint)index >= (uint)value.Length) goto DoneAtEnd;
+                if ((uint)index >= (uint)value.Length)
+                    goto DoneAtEnd;
                 num = value[index];
-                if ((uint)num >= (uint)charToHexLookup.Length || (numValue = charToHexLookup[num]) == 0xFF) goto HasTrailingChars;
+                if ((uint)num >= (uint)charToHexLookup.Length || (numValue = charToHexLookup[num]) == 0xFF)
+                    goto HasTrailingChars;
                 index++;
                 overflow = true;
-                if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+                if ((uint)index >= (uint)value.Length)
+                    goto DoneAtEndButPotentialOverflow;
 
                 // At this point, we're either overflowing or hitting a formatting error.
                 // Format errors take precedence for compatibility. Read through any remaining digits.
@@ -1137,12 +1140,13 @@ namespace System
                 while ((uint)num < (uint)charToHexLookup.Length && charToHexLookup[num] != 0xFF)
                 {
                     index++;
-                    if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+                    if ((uint)index >= (uint)value.Length)
+                        goto DoneAtEndButPotentialOverflow;
                     num = value[index];
                 }
                 goto HasTrailingChars;
             }
+
         FalseExit: // parsing failed
             result = 0;
             return false;
@@ -1161,39 +1165,55 @@ namespace System
             // Skip past trailing whitespace, then past trailing zeros, and if anything else remains, fail.
             if (IsWhite(num))
             {
-                if ((styles & NumberStyles.AllowTrailingWhite) == 0) goto FalseExit;
+                if ((styles & NumberStyles.AllowTrailingWhite) == 0)
+                    goto FalseExit;
                 for (index++; index < value.Length; index++)
                 {
-                    if (!IsWhite(value[index])) break;
+                    if (!IsWhite(value[index]))
+                        break;
                 }
-                if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+                if ((uint)index >= (uint)value.Length)
+                    goto DoneAtEndButPotentialOverflow;
             }
 
-            if (!TrailingZeros(value, index)) goto FalseExit;
-            
+            if (!TrailingZeros(value, index))
+                goto FalseExit;
+
             goto DoneAtEndButPotentialOverflow;
         }
-        
-        internal static bool TryParseUInt64(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out ulong result)
+
+        internal static unsafe bool TryParseUInt64(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out ulong result, out bool failureIsOverflow)
         {
+            result = 0;
+            failureIsOverflow = false;
+
             if ((styles & ~NumberStyles.Integer) == 0)
             {
                 // Optimized path for the common case of anything that's allowed for integer style.
-                bool overflow = false;
-                return TryParseUInt64IntegerStyle(value, styles, info, out result, ref overflow);
+                return TryParseUInt64IntegerStyle(value, styles, info, out result, ref failureIsOverflow);
             }
 
             if ((styles & NumberStyles.AllowHexSpecifier) != 0)
             {
-                bool overflow = false;
-                return TryParseUInt64HexNumberStyle(value, styles, out result, ref overflow);
+                return TryParseUInt64HexNumberStyle(value, styles, out result, ref failureIsOverflow);
             }
 
-            NumberBuffer number = default;
-            result = 0;
-            return
-                TryStringToNumber(value, styles, ref number, info, false) &&
-                NumberToUInt64(ref number, ref result);
+            char* pDigits = stackalloc char[UInt64NumberBufferLength];
+            NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, UInt64NumberBufferLength);
+
+            if (!TryStringToNumber(value, styles, ref number, info))
+            {
+                return false;
+            }
+
+
+            if (!TryNumberToUInt64(ref number, ref result))
+            {
+                failureIsOverflow = true;
+                return false;
+            }
+
+            return true;
         }
 
         /// <summary>Parses ulong limited to styles that make up NumberStyles.Integer.</summary>
@@ -1203,7 +1223,8 @@ namespace System
             Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format");
             Debug.Assert(!failureIsOverflow, $"failureIsOverflow should have been initialized to false");
 
-            if ((uint)value.Length < 1) goto FalseExit;
+            if ((uint)value.Length < 1)
+                goto FalseExit;
 
             bool overflow = false;
             bool hasNegativeSign = false;
@@ -1216,7 +1237,8 @@ namespace System
                 do
                 {
                     index++;
-                    if ((uint)index >= (uint)value.Length) goto FalseExit;
+                    if ((uint)index >= (uint)value.Length)
+                        goto FalseExit;
                     num = value[index];
                 }
                 while (IsWhite(num));
@@ -1232,14 +1254,16 @@ namespace System
                     if (num == '+')
                     {
                         index++;
-                        if ((uint)index >= (uint)value.Length) goto FalseExit;
+                        if ((uint)index >= (uint)value.Length)
+                            goto FalseExit;
                         num = value[index];
                     }
                     else if (num == '-')
                     {
                         hasNegativeSign = true;
                         index++;
-                        if ((uint)index >= (uint)value.Length) goto FalseExit;
+                        if ((uint)index >= (uint)value.Length)
+                            goto FalseExit;
                         num = value[index];
                     }
                 }
@@ -1250,19 +1274,21 @@ namespace System
                     if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign))
                     {
                         index += positiveSign.Length;
-                        if ((uint)index >= (uint)value.Length) goto FalseExit;
+                        if ((uint)index >= (uint)value.Length)
+                            goto FalseExit;
                         num = value[index];
                     }
                     else if (!string.IsNullOrEmpty(negativeSign) && value.StartsWith(negativeSign))
                     {
                         hasNegativeSign = true;
                         index += negativeSign.Length;
-                        if ((uint)index >= (uint)value.Length) goto FalseExit;
+                        if ((uint)index >= (uint)value.Length)
+                            goto FalseExit;
                         num = value[index];
                     }
                 }
             }
+
             long answer = 0;
 
             if (IsDigit(num))
@@ -1273,35 +1299,42 @@ namespace System
                     do
                     {
                         index++;
-                        if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+                        if ((uint)index >= (uint)value.Length)
+                            goto DoneAtEndButPotentialOverflow;
                         num = value[index];
                     } while (num == '0');
-                    if (!IsDigit(num)) goto HasTrailingChars;
+                    if (!IsDigit(num))
+                        goto HasTrailingChars;
                 }
+
                 // Parse most digits, up to the potential for overflow, which can't happen until after 19 digits.
                 answer = num - '0'; // first digit
                 index++;
                 for (int i = 0; i < 18; i++) // next 18 digits can't overflow
                 {
-                    if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+                    if ((uint)index >= (uint)value.Length)
+                        goto DoneAtEndButPotentialOverflow;
                     num = value[index];
-                    if (!IsDigit(num)) goto HasTrailingChars;
+                    if (!IsDigit(num))
+                        goto HasTrailingChars;
                     index++;
                     answer = 10 * answer + num - '0';
                 }
 
                 // Potential overflow now processing the 20th digit.
-                if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+                if ((uint)index >= (uint)value.Length)
+                    goto DoneAtEndButPotentialOverflow;
                 num = value[index];
-                if (!IsDigit(num)) goto HasTrailingChars;
+                if (!IsDigit(num))
+                    goto HasTrailingChars;
                 index++;
                 if ((ulong)answer > ulong.MaxValue / 10 || ((ulong)answer == ulong.MaxValue / 10 && num > '5'))
                 {
                     overflow = true;
                 }
                 answer = answer * 10 + num - '0';
-                if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+                if ((uint)index >= (uint)value.Length)
+                    goto DoneAtEndButPotentialOverflow;
 
                 // At this point, we're either overflowing or hitting a formatting error.
                 // Format errors take precedence for compatibility.
@@ -1310,12 +1343,13 @@ namespace System
                 {
                     overflow = true;
                     index++;
-                    if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+                    if ((uint)index >= (uint)value.Length)
+                        goto DoneAtEndButPotentialOverflow;
                     num = value[index];
                 }
                 goto HasTrailingChars;
             }
+
         FalseExit: // parsing failed
             result = 0;
             return false;
@@ -1333,16 +1367,20 @@ namespace System
             // Skip past trailing whitespace, then past trailing zeros, and if anything else remains, fail.
             if (IsWhite(num))
             {
-                if ((styles & NumberStyles.AllowTrailingWhite) == 0) goto FalseExit;
+                if ((styles & NumberStyles.AllowTrailingWhite) == 0)
+                    goto FalseExit;
                 for (index++; index < value.Length; index++)
                 {
-                    if (!IsWhite(value[index])) break;
+                    if (!IsWhite(value[index]))
+                        break;
                 }
-                if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+                if ((uint)index >= (uint)value.Length)
+                    goto DoneAtEndButPotentialOverflow;
             }
 
-            if (!TrailingZeros(value, index)) goto FalseExit;
-            
+            if (!TrailingZeros(value, index))
+                goto FalseExit;
+
             goto DoneAtEndButPotentialOverflow;
         }
 
@@ -1353,7 +1391,8 @@ namespace System
             Debug.Assert((styles & ~NumberStyles.HexNumber) == 0, "Only handles subsets of HexNumber format");
             Debug.Assert(!failureIsOverflow, $"failureIsOverflow should have been initialized to false");
 
-            if ((uint)value.Length < 1) goto FalseExit;
+            if ((uint)value.Length < 1)
+                goto FalseExit;
 
             bool overflow = false;
             int index = 0;
@@ -1366,7 +1405,8 @@ namespace System
                 do
                 {
                     index++;
-                    if ((uint)index >= (uint)value.Length) goto FalseExit;
+                    if ((uint)index >= (uint)value.Length)
+                        goto FalseExit;
                     num = value[index];
                 }
                 while (IsWhite(num));
@@ -1383,31 +1423,38 @@ namespace System
                     do
                     {
                         index++;
-                        if ((uint)index >= (uint)value.Length) goto DoneAtEnd;
+                        if ((uint)index >= (uint)value.Length)
+                            goto DoneAtEnd;
                         num = value[index];
                     } while (num == '0');
-                    if ((uint)num >= (uint)charToHexLookup.Length || charToHexLookup[num] == 0xFF) goto HasTrailingChars;
+                    if ((uint)num >= (uint)charToHexLookup.Length || charToHexLookup[num] == 0xFF)
+                        goto HasTrailingChars;
                 }
+
                 // Parse up through 16 digits, as no overflow is possible
                 answer = charToHexLookup[num]; // first digit
                 index++;
                 for (int i = 0; i < 15; i++) // next 15 digits can't overflow
                 {
-                    if ((uint)index >= (uint)value.Length) goto DoneAtEnd;
+                    if ((uint)index >= (uint)value.Length)
+                        goto DoneAtEnd;
                     num = value[index];
-                    if ((uint)num >= (uint)charToHexLookup.Length || (numValue = charToHexLookup[num]) == 0xFF) goto HasTrailingChars;
+                    if ((uint)num >= (uint)charToHexLookup.Length || (numValue = charToHexLookup[num]) == 0xFF)
+                        goto HasTrailingChars;
                     index++;
                     answer = 16 * answer + numValue;
                 }
 
                 // If there's another digit, it's an overflow.
-                if ((uint)index >= (uint)value.Length) goto DoneAtEnd;
+                if ((uint)index >= (uint)value.Length)
+                    goto DoneAtEnd;
                 num = value[index];
-                if ((uint)num >= (uint)charToHexLookup.Length || (numValue = charToHexLookup[num]) == 0xFF) goto HasTrailingChars;
+                if ((uint)num >= (uint)charToHexLookup.Length || (numValue = charToHexLookup[num]) == 0xFF)
+                    goto HasTrailingChars;
                 index++;
                 overflow = true;
-                if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+                if ((uint)index >= (uint)value.Length)
+                    goto DoneAtEndButPotentialOverflow;
 
                 // At this point, we're either overflowing or hitting a formatting error.
                 // Format errors take precedence for compatibility. Read through any remaining digits.
@@ -1415,12 +1462,13 @@ namespace System
                 while ((uint)num < (uint)charToHexLookup.Length && charToHexLookup[num] != 0xFF)
                 {
                     index++;
-                    if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+                    if ((uint)index >= (uint)value.Length)
+                        goto DoneAtEndButPotentialOverflow;
                     num = value[index];
                 }
                 goto HasTrailingChars;
             }
+
         FalseExit: // parsing failed
             result = 0;
             return false;
@@ -1439,38 +1487,38 @@ namespace System
             // Skip past trailing whitespace, then past trailing zeros, and if anything else remains, fail.
             if (IsWhite(num))
             {
-                if ((styles & NumberStyles.AllowTrailingWhite) == 0) goto FalseExit;
+                if ((styles & NumberStyles.AllowTrailingWhite) == 0)
+                    goto FalseExit;
                 for (index++; index < value.Length; index++)
                 {
-                    if (!IsWhite(value[index])) break;
+                    if (!IsWhite(value[index]))
+                        break;
                 }
-                if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+                if ((uint)index >= (uint)value.Length)
+                    goto DoneAtEndButPotentialOverflow;
             }
 
-            if (!TrailingZeros(value, index)) goto FalseExit;
-            
+            if (!TrailingZeros(value, index))
+                goto FalseExit;
+
             goto DoneAtEndButPotentialOverflow;
         }
 
         internal static decimal ParseDecimal(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
         {
-            NumberBuffer number = default;
-            decimal result = 0;
-
-            StringToNumber(value, styles, ref number, info, true);
-
-            if (!NumberBufferToDecimal(ref number, ref result))
+            if (!TryParseDecimal(value, styles, info, out decimal result, out bool failureIsOverflow))
             {
-                ThrowOverflowOrFormatException(overflow: true, nameof(SR.Overflow_Decimal));
+                ThrowOverflowOrFormatException(failureIsOverflow, nameof(SR.Overflow_Decimal));
             }
+
             return result;
         }
 
-        private static unsafe bool NumberBufferToDecimal(ref NumberBuffer number, ref decimal value)
+        private static unsafe bool TryNumberToDecimal(ref NumberBuffer number, ref decimal value)
         {
             char* p = number.GetDigitsPointer();
-            int e = number.scale;
-            bool sign = number.sign;
+            int e = number.Scale;
+            bool sign = number.Sign;
             uint c = *p;
             if (c == 0)
             {
@@ -1569,154 +1617,123 @@ namespace System
 
         internal static double ParseDouble(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
         {
-            NumberBuffer number = default;
-            double d = 0;
-
-            if (!TryStringToNumber(value, styles, ref number, info, false))
+            if (!TryParseDouble(value, styles, info, out double result, out bool failureIsOverflow))
             {
-                //If we failed TryStringToNumber, it may be from one of our special strings.
-                //Check the three with which we're concerned and rethrow if it's not one of
-                //those strings.
-                ReadOnlySpan<char> sTrim = value.Trim();
-                if (sTrim.EqualsOrdinal(info.PositiveInfinitySymbol))
-                {
-                    return double.PositiveInfinity;
-                }
-                if (sTrim.EqualsOrdinal(info.NegativeInfinitySymbol))
-                {
-                    return double.NegativeInfinity;
-                }
-                if (sTrim.EqualsOrdinal(info.NaNSymbol))
-                {
-                    return double.NaN;
-                }
-                ThrowOverflowOrFormatException(overflow: false, null);
+                ThrowOverflowOrFormatException(failureIsOverflow, nameof(SR.Overflow_Double));
             }
 
-            if (!NumberBufferToDouble(ref number, ref d))
-            {
-                ThrowOverflowOrFormatException(overflow: true, nameof(SR.Overflow_Double));
-            }
-
-            return d;
+            return result;
         }
 
         internal static float ParseSingle(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
         {
-            NumberBuffer number = default;
-            double d = 0;
-
-            if (!TryStringToNumber(value, styles, ref number, info, false))
+            if (!TryParseSingle(value, styles, info, out float result, out bool failureIsOverflow))
             {
-                //If we failed TryStringToNumber, it may be from one of our special strings.
-                //Check the three with which we're concerned and rethrow if it's not one of
-                //those strings.
-                ReadOnlySpan<char> sTrim = value.Trim();
-                if (sTrim.EqualsOrdinal(info.PositiveInfinitySymbol))
-                {
-                    return float.PositiveInfinity;
-                }
-                if (sTrim.EqualsOrdinal(info.NegativeInfinitySymbol))
-                {
-                    return float.NegativeInfinity;
-                }
-                if (sTrim.EqualsOrdinal(info.NaNSymbol))
-                {
-                    return float.NaN;
-                }
-                ThrowOverflowOrFormatException(overflow: false, null);
+                ThrowOverflowOrFormatException(failureIsOverflow, nameof(SR.Overflow_Single));
             }
 
-            if (!NumberBufferToDouble(ref number, ref d))
-            {
-                ThrowOverflowOrFormatException(overflow: true, nameof(SR.Overflow_Single));
-            }
-            float castSingle = (float)d;
-            if (float.IsInfinity(castSingle))
-            {
-                ThrowOverflowOrFormatException(overflow: true, nameof(SR.Overflow_Single));
-            }
-            return castSingle;
+            return result;
         }
 
-        internal static bool TryParseDecimal(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out decimal result)
+        internal static unsafe bool TryParseDecimal(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out decimal result, out bool failureIsOverflow)
         {
-            NumberBuffer number = default;
+            char* pDigits = stackalloc char[DecimalNumberBufferLength];
+            NumberBuffer number = new NumberBuffer(NumberBufferKind.Decimal, pDigits, DecimalNumberBufferLength);
+
             result = 0;
+            failureIsOverflow = false;
 
-            if (!TryStringToNumber(value, styles, ref number, info, true))
+            if (!TryStringToNumber(value, styles, ref number, info))
             {
                 return false;
             }
 
-            if (!NumberBufferToDecimal(ref number, ref result))
+            if (!TryNumberToDecimal(ref number, ref result))
             {
+                failureIsOverflow = true;
                 return false;
             }
+
             return true;
         }
 
-        internal static bool TryParseDouble(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out double result)
+        internal static unsafe bool TryParseDouble(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out double result, out bool failureIsOverflow)
         {
-            NumberBuffer number = default;
+            char* pDigits = stackalloc char[DoubleNumberBufferLength];
+            NumberBuffer number = new NumberBuffer(NumberBufferKind.Double, pDigits, DoubleNumberBufferLength);
+
             result = 0;
+            failureIsOverflow = false;
 
-            if (!TryStringToNumber(value, styles, ref number, info, false))
+            if (!TryStringToNumber(value, styles, ref number, info))
             {
-                return false;
+                ReadOnlySpan<char> valueTrim = value.Trim();
+
+                if (valueTrim.EqualsOrdinal(info.PositiveInfinitySymbol))
+                {
+                    result = double.PositiveInfinity;
+                }
+                else if (valueTrim.EqualsOrdinal(info.NegativeInfinitySymbol))
+                {
+                    result = double.NegativeInfinity;
+                }
+                else if (valueTrim.EqualsOrdinal(info.NaNSymbol))
+                {
+                    result = double.NaN;
+                }
+                else
+                {
+                    return false; // We really failed
+                }
+
+                return true;
             }
-            if (!NumberBufferToDouble(ref number, ref result))
+
+            if (!TryNumberToDouble(ref number, ref result))
             {
+                failureIsOverflow = true;
                 return false;
             }
+
             return true;
         }
 
-        internal static bool TryParseSingle(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out float result)
+        internal static bool TryParseSingle(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out float result, out bool failureIsOverflow)
         {
-            NumberBuffer number = default;
             result = 0;
-            double d = 0;
 
-            if (!TryStringToNumber(value, styles, ref number, info, false))
-            {
-                return false;
-            }
-            if (!NumberBufferToDouble(ref number, ref d))
+            if (!TryParseDouble(value, styles, info, out double doubleResult, out failureIsOverflow))
             {
                 return false;
             }
-            float castSingle = (float)d;
-            if (float.IsInfinity(castSingle))
+
+            float singleResult = (float)(doubleResult);
+
+            if (float.IsInfinity(singleResult) && double.IsFinite(doubleResult))
             {
+                failureIsOverflow = true;
                 return false;
             }
 
-            result = castSingle;
+            result = singleResult;
             return true;
         }
 
-        private static unsafe void StringToNumber(ReadOnlySpan<char> value, NumberStyles styles, ref NumberBuffer number, NumberFormatInfo info, bool parseDecimal)
+        private static unsafe void StringToNumber(ReadOnlySpan<char> value, NumberStyles styles, ref NumberBuffer number, NumberFormatInfo info)
         {
-            Debug.Assert(info != null);
-            fixed (char* stringPointer = &MemoryMarshal.GetReference(value))
+            if (!TryStringToNumber(value, styles, ref number, info))
             {
-                char* p = stringPointer;
-                if (!ParseNumber(ref p, p + value.Length, styles, ref number, info, parseDecimal)
-                    || (p - stringPointer < value.Length && !TrailingZeros(value, (int)(p - stringPointer))))
-                {
-                    ThrowOverflowOrFormatException(overflow: false, null);
-                }
+                ThrowOverflowOrFormatException(overflow: false, null);
             }
         }
 
-        internal static unsafe bool TryStringToNumber(ReadOnlySpan<char> value, NumberStyles styles, ref NumberBuffer number, NumberFormatInfo info, bool parseDecimal)
+        internal static unsafe bool TryStringToNumber(ReadOnlySpan<char> value, NumberStyles styles, ref NumberBuffer number, NumberFormatInfo info)
         {
             Debug.Assert(info != null);
             fixed (char* stringPointer = &MemoryMarshal.GetReference(value))
             {
                 char* p = stringPointer;
-                if (!ParseNumber(ref p, p + value.Length, styles, ref number, info, parseDecimal)
+                if (!TryParseNumber(ref p, p + value.Length, styles, ref number, info)
                     || (p - stringPointer < value.Length && !TrailingZeros(value, (int)(p - stringPointer))))
                 {
                     return false;
@@ -1760,7 +1777,8 @@ namespace System
                         }
                         p++;
                         str++;
-                        if (*str == '\0') return p;
+                        if (*str == '\0')
+                            return p;
                     }
                 }
             }
@@ -1779,7 +1797,7 @@ namespace System
                (Exception)new FormatException(SR.Format_InvalidString);
         }
 
-        private static bool NumberBufferToDouble(ref NumberBuffer number, ref double value)
+        private static bool TryNumberToDouble(ref NumberBuffer number, ref double value)
         {
             double d = NumberToDouble(ref number);
             if (!double.IsFinite(d))
index e347e3b..d311484 100644 (file)
@@ -221,7 +221,7 @@ namespace System
         {
             result = 0;
             int i;
-            if (!Number.TryParseInt32(s, style, info, out i))
+            if (!Number.TryParseInt32(s, style, info, out i, out _))
             {
                 return false;
             }
index 8d1788f..dfe1af3 100644 (file)
@@ -330,28 +330,7 @@ namespace System
 
         private static bool TryParse(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info, out float result)
         {
-            bool success = Number.TryParseSingle(s, style, info, out result);
-            if (!success)
-            {
-                ReadOnlySpan<char> sTrim = s.Trim();
-                if (sTrim.EqualsOrdinal(info.PositiveInfinitySymbol))
-                {
-                    result = PositiveInfinity;
-                }
-                else if (sTrim.EqualsOrdinal(info.NegativeInfinitySymbol))
-                {
-                    result = NegativeInfinity;
-                }
-                else if (sTrim.EqualsOrdinal(info.NaNSymbol))
-                {
-                    result = NaN;
-                }
-                else
-                {
-                    return false; // We really failed
-                }
-            }
-            return true;
+            return Number.TryParseSingle(s, style, info, out result, out _);
         }
 
         //
index f9ef1f6..38c8b97 100644 (file)
@@ -190,7 +190,7 @@ namespace System
         {
             result = 0;
             uint i;
-            if (!Number.TryParseUInt32(s, style, info, out i))
+            if (!Number.TryParseUInt32(s, style, info, out i, out _))
             {
                 return false;
             }
index 5ed193e..576b55f 100644 (file)
@@ -148,13 +148,13 @@ namespace System
                 return false;
             }
 
-            return Number.TryParseUInt32(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result);
+            return Number.TryParseUInt32(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result, out _);
         }
 
         [CLSCompliant(false)]
         public static bool TryParse(ReadOnlySpan<char> s, out uint result)
         {
-            return Number.TryParseUInt32(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result);
+            return Number.TryParseUInt32(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result, out _);
         }
 
         [CLSCompliant(false)]
@@ -168,14 +168,14 @@ namespace System
                 return false;
             }
 
-            return Number.TryParseUInt32(s, style, NumberFormatInfo.GetInstance(provider), out result);
+            return Number.TryParseUInt32(s, style, NumberFormatInfo.GetInstance(provider), out result, out _);
         }
 
         [CLSCompliant(false)]
         public static bool TryParse(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider provider, out uint result)
         {
             NumberFormatInfo.ValidateParseStyleInteger(style);
-            return Number.TryParseUInt32(s, style, NumberFormatInfo.GetInstance(provider), out result);
+            return Number.TryParseUInt32(s, style, NumberFormatInfo.GetInstance(provider), out result, out _);
         }
 
         //
index 6abd76d..0abe8d4 100644 (file)
@@ -145,13 +145,13 @@ namespace System
                 return false;
             }
 
-            return Number.TryParseUInt64(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result);
+            return Number.TryParseUInt64(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result, out _);
         }
 
         [CLSCompliant(false)]
         public static bool TryParse(ReadOnlySpan<char> s, out ulong result)
         {
-            return Number.TryParseUInt64(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result);
+            return Number.TryParseUInt64(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result, out _);
         }
 
         [CLSCompliant(false)]
@@ -165,14 +165,14 @@ namespace System
                 return false;
             }
 
-            return Number.TryParseUInt64(s, style, NumberFormatInfo.GetInstance(provider), out result);
+            return Number.TryParseUInt64(s, style, NumberFormatInfo.GetInstance(provider), out result, out _);
         }
 
         [CLSCompliant(false)]
         public static bool TryParse(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider provider, out ulong result)
         {
             NumberFormatInfo.ValidateParseStyleInteger(style);
-            return Number.TryParseUInt64(s, style, NumberFormatInfo.GetInstance(provider), out result);
+            return Number.TryParseUInt64(s, style, NumberFormatInfo.GetInstance(provider), out result, out _);
         }
 
         //