Optimize {Try}FormatDecimal (dotnet/coreclr#18935)
authorPent Ploompuu <kaalikas@gmail.com>
Wed, 18 Jul 2018 10:14:49 +0000 (13:14 +0300)
committerJan Kotas <jkotas@microsoft.com>
Wed, 18 Jul 2018 10:14:49 +0000 (03:14 -0700)
Commit migrated from https://github.com/dotnet/coreclr/commit/fb37382267a5e015769a3b4ef127e5544fe642c7

src/libraries/System.Private.CoreLib/src/System/Number.Formatting.cs
src/libraries/System.Private.CoreLib/src/System/Text/ValueStringBuilder.cs

index 1ff6982..689f146 100644 (file)
@@ -290,7 +290,7 @@ namespace System
             char fmt = ParseFormatSpecifier(format, out int digits);
 
             NumberBuffer number = default;
-            DecimalToNumber(value, ref number);
+            DecimalToNumber(ref value, ref number);
 
             ValueStringBuilder sb;
             unsafe
@@ -316,7 +316,7 @@ namespace System
             char fmt = ParseFormatSpecifier(format, out int digits);
 
             NumberBuffer number = default;
-            DecimalToNumber(value, ref number);
+            DecimalToNumber(ref value, ref number);
 
             ValueStringBuilder sb;
             unsafe
@@ -337,10 +337,8 @@ namespace System
             return sb.TryCopyTo(destination, out charsWritten);
         }
 
-        private static unsafe void DecimalToNumber(decimal value, ref NumberBuffer number)
+        private static unsafe void DecimalToNumber(ref decimal d, ref NumberBuffer number)
         {
-            decimal d = value;
-
             char* buffer = number.digits;
             number.precision = DecimalPrecision;
             number.sign = d.IsNegative;
@@ -352,7 +350,7 @@ namespace System
             }
             p = UInt32ToDecChars(p, d.Low, 0);
 
-            int i = (int)(buffer + DecimalPrecision - p);
+            int i = (int)((byte*)(buffer + DecimalPrecision) - (byte*)p) >> 1;
             number.scale = i - d.Scale;
 
             char* dst = number.digits;
@@ -1465,20 +1463,17 @@ namespace System
 
         internal static unsafe void NumberToString(ref ValueStringBuilder sb, ref NumberBuffer number, char format, int nMaxDigits, NumberFormatInfo info, bool isDecimal)
         {
-            int nMinDigits = -1;
-
             switch (format)
             {
                 case 'C':
                 case 'c':
                     {
-                        nMinDigits = nMaxDigits >= 0 ? nMaxDigits : info.CurrencyDecimalDigits;
                         if (nMaxDigits < 0)
                             nMaxDigits = info.CurrencyDecimalDigits;
 
                         RoundNumber(ref number, number.scale + nMaxDigits); // Don't change this line to use digPos since digCount could have its sign changed.
 
-                        FormatCurrency(ref sb, ref number, nMinDigits, nMaxDigits, info);
+                        FormatCurrency(ref sb, ref number, nMaxDigits, info);
 
                         break;
                     }
@@ -1487,16 +1482,14 @@ namespace System
                 case 'f':
                     {
                         if (nMaxDigits < 0)
-                            nMaxDigits = nMinDigits = info.NumberDecimalDigits;
-                        else
-                            nMinDigits = nMaxDigits;
+                            nMaxDigits = info.NumberDecimalDigits;
 
                         RoundNumber(ref number, number.scale + nMaxDigits);
 
                         if (number.sign)
                             sb.Append(info.NegativeSign);
 
-                        FormatFixed(ref sb, ref number, nMinDigits, nMaxDigits, info, null, info.NumberDecimalSeparator, null);
+                        FormatFixed(ref sb, ref number, nMaxDigits, info, null, info.NumberDecimalSeparator, null);
 
                         break;
                     }
@@ -1505,13 +1498,11 @@ namespace System
                 case 'n':
                     {
                         if (nMaxDigits < 0)
-                            nMaxDigits = nMinDigits = info.NumberDecimalDigits; // Since we are using digits in our calculation
-                        else
-                            nMinDigits = nMaxDigits;
+                            nMaxDigits = info.NumberDecimalDigits; // Since we are using digits in our calculation
 
                         RoundNumber(ref number, number.scale + nMaxDigits);
 
-                        FormatNumber(ref sb, ref number, nMinDigits, nMaxDigits, info);
+                        FormatNumber(ref sb, ref number, nMaxDigits, info);
 
                         break;
                     }
@@ -1520,9 +1511,7 @@ namespace System
                 case 'e':
                     {
                         if (nMaxDigits < 0)
-                            nMaxDigits = nMinDigits = 6;
-                        else
-                            nMinDigits = nMaxDigits;
+                            nMaxDigits = 6;
                         nMaxDigits++;
 
                         RoundNumber(ref number, nMaxDigits);
@@ -1530,7 +1519,7 @@ namespace System
                         if (number.sign)
                             sb.Append(info.NegativeSign);
 
-                        FormatScientific(ref sb, ref number, nMinDigits, nMaxDigits, info, format);
+                        FormatScientific(ref sb, ref number, nMaxDigits, info, format);
 
                         break;
                     }
@@ -1538,40 +1527,34 @@ namespace System
                 case 'G':
                 case 'g':
                     {
-                        bool enableRounding = true;
+                        bool noRounding = false;
                         if (nMaxDigits < 1)
                         {
                             if (isDecimal && (nMaxDigits == -1))
                             {
-                                // Default to 29 digits precision only for G formatting without a precision specifier
-                                // This ensures that the PAL code pads out to the correct place even when we use the default precision
-                                nMaxDigits = nMinDigits = DecimalPrecision;
-                                enableRounding = false;  // Turn off rounding for ECMA compliance to output trailing 0's after decimal as significant
+                                noRounding = true;  // Turn off rounding for ECMA compliance to output trailing 0's after decimal as significant
+                                if (number.digits[0] == 0)
+                                {
+                                    // Minus zero should be formatted as 0
+                                    goto SkipSign;
+                                }
+                                goto SkipRounding;
                             }
                             else
                             {
                                 // This ensures that the PAL code pads out to the correct place even when we use the default precision
-                                nMaxDigits = nMinDigits = number.precision;
+                                nMaxDigits = number.precision;
                             }
                         }
-                        else
-                            nMinDigits = nMaxDigits;
 
-                        if (enableRounding) // Don't round for G formatting without precision
-                            RoundNumber(ref number, nMaxDigits); // This also fixes up the minus zero case
-                        else
-                        {
-                            if (isDecimal && (number.digits[0] == 0))
-                            {
-                                // Minus zero should be formatted as 0
-                                number.sign = false;
-                            }
-                        }
+                        RoundNumber(ref number, nMaxDigits); // This also fixes up the minus zero case
 
+SkipRounding:
                         if (number.sign)
                             sb.Append(info.NegativeSign);
 
-                        FormatGeneral(ref sb, ref number, nMinDigits, nMaxDigits, info, (char)(format - ('G' - 'E')), !enableRounding);
+SkipSign:
+                        FormatGeneral(ref sb, ref number, nMaxDigits, info, (char)(format - ('G' - 'E')), noRounding);
 
                         break;
                     }
@@ -1580,14 +1563,12 @@ namespace System
                 case 'p':
                     {
                         if (nMaxDigits < 0)
-                            nMaxDigits = nMinDigits = info.PercentDecimalDigits;
-                        else
-                            nMinDigits = nMaxDigits;
+                            nMaxDigits = info.PercentDecimalDigits;
                         number.scale += 2;
 
                         RoundNumber(ref number, number.scale + nMaxDigits);
 
-                        FormatPercent(ref sb, ref number, nMinDigits, nMaxDigits, info);
+                        FormatPercent(ref sb, ref number, nMaxDigits, info);
 
                         break;
                     }
@@ -1954,7 +1935,7 @@ namespace System
             }
         }
 
-        private static void FormatCurrency(ref ValueStringBuilder sb, ref NumberBuffer number, int nMinDigits, int nMaxDigits, NumberFormatInfo info)
+        private static void FormatCurrency(ref ValueStringBuilder sb, ref NumberBuffer number, int nMaxDigits, NumberFormatInfo info)
         {
             string fmt = number.sign ?
                 s_negCurrencyFormats[info.CurrencyNegativePattern] :
@@ -1965,7 +1946,7 @@ namespace System
                 switch (ch)
                 {
                     case '#':
-                        FormatFixed(ref sb, ref number, nMinDigits, nMaxDigits, info, info.currencyGroupSizes, info.CurrencyDecimalSeparator, info.CurrencyGroupSeparator);
+                        FormatFixed(ref sb, ref number, nMaxDigits, info, info.currencyGroupSizes, info.CurrencyDecimalSeparator, info.CurrencyGroupSeparator);
                         break;
                     case '-':
                         sb.Append(info.NegativeSign);
@@ -1980,7 +1961,7 @@ namespace System
             }
         }
 
-        private static unsafe void FormatFixed(ref ValueStringBuilder sb, ref NumberBuffer number, int nMinDigits, int nMaxDigits, NumberFormatInfo info, int[] groupDigits, string sDecimal, string sGroup)
+        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;
             char* dig = number.digits;
@@ -2082,7 +2063,7 @@ namespace System
             }
         }
 
-        private static void FormatNumber(ref ValueStringBuilder sb, ref NumberBuffer number, int nMinDigits, int nMaxDigits, NumberFormatInfo info)
+        private static void FormatNumber(ref ValueStringBuilder sb, ref NumberBuffer number, int nMaxDigits, NumberFormatInfo info)
         {
             string fmt = number.sign ?
                 s_negNumberFormats[info.NumberNegativePattern] :
@@ -2093,7 +2074,7 @@ namespace System
                 switch (ch)
                 {
                     case '#':
-                        FormatFixed(ref sb, ref number, nMinDigits, nMaxDigits, info, info.numberGroupSizes, info.NumberDecimalSeparator, info.NumberGroupSeparator);
+                        FormatFixed(ref sb, ref number, nMaxDigits, info, info.numberGroupSizes, info.NumberDecimalSeparator, info.NumberGroupSeparator);
                         break;
                     case '-':
                         sb.Append(info.NegativeSign);
@@ -2105,7 +2086,7 @@ namespace System
             }
         }
 
-        private static unsafe void FormatScientific(ref ValueStringBuilder sb, ref NumberBuffer number, int nMinDigits, int nMaxDigits, NumberFormatInfo info, char expChar)
+        private static unsafe void FormatScientific(ref ValueStringBuilder sb, ref NumberBuffer number, int nMaxDigits, NumberFormatInfo info, char expChar)
         {
             char* dig = number.digits;
 
@@ -2142,7 +2123,7 @@ namespace System
             sb.Append(p, (int)(digits + MaxUInt32DecDigits - p));
         }
 
-        private static unsafe void FormatGeneral(ref ValueStringBuilder sb, ref NumberBuffer number, int nMinDigits, int nMaxDigits, NumberFormatInfo info, char expChar, bool bSuppressScientific)
+        private static unsafe void FormatGeneral(ref ValueStringBuilder sb, ref NumberBuffer number, int nMaxDigits, NumberFormatInfo info, char expChar, bool bSuppressScientific)
         {
             int digPos = number.scale;
             bool scientific = false;
@@ -2189,7 +2170,7 @@ namespace System
                 FormatExponent(ref sb, info, number.scale - 1, expChar, 2, true);
         }
 
-        private static void FormatPercent(ref ValueStringBuilder sb, ref NumberBuffer number, int nMinDigits, int nMaxDigits, NumberFormatInfo info)
+        private static void FormatPercent(ref ValueStringBuilder sb, ref NumberBuffer number, int nMaxDigits, NumberFormatInfo info)
         {
             string fmt = number.sign ?
                 s_negPercentFormats[info.PercentNegativePattern] :
@@ -2200,7 +2181,7 @@ namespace System
                 switch (ch)
                 {
                     case '#':
-                        FormatFixed(ref sb, ref number, nMinDigits, nMaxDigits, info, info.percentGroupSizes, info.PercentDecimalSeparator, info.PercentGroupSeparator);
+                        FormatFixed(ref sb, ref number, nMaxDigits, info, info.percentGroupSizes, info.PercentDecimalSeparator, info.PercentGroupSeparator);
                         break;
                     case '-':
                         sb.Append(info.NegativeSign);
index 0448837..045a40b 100644 (file)
@@ -132,7 +132,7 @@ namespace System.Text
         public void Append(char c)
         {
             int pos = _pos;
-            if (pos < _chars.Length)
+            if ((uint)pos < (uint)_chars.Length)
             {
                 _chars[pos] = c;
                 _pos = pos + 1;
@@ -147,7 +147,7 @@ namespace System.Text
         public void Append(string s)
         {
             int pos = _pos;
-            if (s.Length == 1 && pos < _chars.Length) // very common case, e.g. appending strings from NumberFormatInfo like separators, percent symbols, etc.
+            if (s.Length == 1 && (uint)pos < (uint)_chars.Length) // very common case, e.g. appending strings from NumberFormatInfo like separators, percent symbols, etc.
             {
                 _chars[pos] = s[0];
                 _pos = pos + 1;