String.Split(char[],int,StringSplitOptions) should remove empty entries while
[mono.git] / mcs / class / corlib / System / NumberFormatter.cs
index 18cf4eca0f144bd8bd25ae95e62a2b0966195288..4ff029cd761cf6be98651b960ba07c9b7dd129e5 100644 (file)
@@ -19,45 +19,112 @@ namespace System
                #region NumberToString
                public static string NumberToString (string format, sbyte value, NumberFormatInfo nfi)
                {
-                       return NumberToString (format, new NumberStore (value), nfi);
+                       char specifier;
+                       int precision;
+                       bool custom;
+                       ParseBasicFormat (format, out specifier, out precision, out custom);
+                       if (!custom && (specifier == 'x' || specifier == 'X'))
+                               return FormatHexadecimal (value >= 0 ? (ulong)value : (ulong)-value, value >= 0, 1, precision, specifier == 'X');
+                       return NumberToString (format, new NumberStore (value), nfi, specifier, precision, custom);
                }
                public static string NumberToString (string format, byte value, NumberFormatInfo nfi)
                {
-                       return NumberToString (format, new NumberStore (value), nfi);
+                       char specifier;
+                       int precision;
+                       bool custom;
+                       ParseBasicFormat (format, out specifier, out precision, out custom);
+                       if (!custom && (specifier == 'x' || specifier == 'X'))
+                               return FormatHexadecimal (value, true, 1, precision, specifier == 'X');
+                       return NumberToString (format, new NumberStore (value), nfi, specifier, precision, custom);
                }
                public static string NumberToString (string format, ushort value, NumberFormatInfo nfi)
                {
-                       return NumberToString (format, new NumberStore (value), nfi);
+                       char specifier;
+                       int precision;
+                       bool custom;
+                       ParseBasicFormat (format, out specifier, out precision, out custom);
+                       if (!custom && (specifier == 'x' || specifier == 'X'))
+                               return FormatHexadecimal (value, true, 2, precision, specifier == 'X');
+                       return NumberToString (format, new NumberStore (value), nfi, specifier, precision, custom);
                }
                public static string NumberToString (string format, short value, NumberFormatInfo nfi)
                {
-                       return NumberToString (format, new NumberStore (value), nfi);
+                       char specifier;
+                       int precision;
+                       bool custom;
+                       ParseBasicFormat (format, out specifier, out precision, out custom);
+                       if (!custom && (specifier == 'x' || specifier == 'X'))
+                               return FormatHexadecimal (value >= 0 ? (ulong)value : (ulong)-value, value >= 0, 2, precision, specifier == 'X');
+                       return NumberToString (format, new NumberStore (value), nfi, specifier, precision, custom);
                }
                public static string NumberToString (string format, uint value, NumberFormatInfo nfi)
                {
-                       return NumberToString (format, new NumberStore (value), nfi);
+                       char specifier;
+                       int precision;
+                       bool custom;
+                       ParseBasicFormat (format, out specifier, out precision, out custom);
+                       if (!custom && (specifier == 'x' || specifier == 'X'))
+                               return FormatHexadecimal (value, true, 4, precision, specifier == 'X');
+                       return NumberToString (format, new NumberStore (value), nfi, specifier, precision, custom);
                }
                public static string NumberToString (string format, int value, NumberFormatInfo nfi)
                {
-                       return NumberToString (format, new NumberStore (value), nfi);
+                       char specifier;
+                       int precision;
+                       bool custom;
+                       ParseBasicFormat (format, out specifier, out precision, out custom);
+                       if (!custom && (specifier == 'x' || specifier == 'X'))
+                               return FormatHexadecimal (value >= 0 ? (ulong)value : (ulong)-(long)value, value >= 0, 4, precision, specifier == 'X');
+                       return NumberToString (format, new NumberStore (value), nfi, specifier, precision, custom);
                }
                public static string NumberToString (string format, ulong value, NumberFormatInfo nfi)
                {
-                       return NumberToString (format, new NumberStore (value), nfi);
+                       char specifier;
+                       int precision;
+                       bool custom;
+                       ParseBasicFormat (format, out specifier, out precision, out custom);
+                       if (!custom && (specifier == 'x' || specifier == 'X'))
+                               return FormatHexadecimal (value, true, 8, precision, specifier == 'X');
+                       return NumberToString (format, new NumberStore (value), nfi, specifier, precision, custom);
                }
                public static string NumberToString (string format, long value, NumberFormatInfo nfi)
                {
-                       return NumberToString (format, new NumberStore (value), nfi);
+                       char specifier;
+                       int precision;
+                       bool custom;
+                       ParseBasicFormat (format, out specifier, out precision, out custom);
+                       if (!custom && (specifier == 'x' || specifier == 'X'))
+                               return FormatHexadecimal (value >= 0 ? (ulong)value : (ulong)-value, value >= 0, 8, precision, specifier == 'X');
+                       return NumberToString (format, new NumberStore (value), nfi, specifier, precision, custom);
                }
                public static string NumberToString (string format, float value, NumberFormatInfo nfi)
                {
-                       return NumberToString (format, new NumberStore (value), nfi);
+                       char specifier;
+                       int precision;
+                       bool custom;
+                       ParseBasicFormat (format, out specifier, out precision, out custom);
+                       if (!custom && (specifier == 'x' || specifier == 'X')) throw new FormatException ();
+                       return NumberToString (format, new NumberStore (value), nfi, specifier, precision, custom);
                }
                public static string NumberToString (string format, double value, NumberFormatInfo nfi)
                {
-                       return NumberToString (format, new NumberStore (value), nfi);
+                       char specifier;
+                       int precision;
+                       bool custom;
+                       ParseBasicFormat (format, out specifier, out precision, out custom);
+                       if (!custom && (specifier == 'x' || specifier == 'X')) throw new FormatException ();
+                       return NumberToString (format, new NumberStore (value), nfi, specifier, precision, custom);
+               }
+               public static string NumberToString (string format, decimal value, NumberFormatInfo nfi)
+               {
+                       char specifier;
+                       int precision;
+                       bool custom;
+                       ParseBasicFormat (format, out specifier, out precision, out custom);
+                       if (!custom && (specifier == 'x' || specifier == 'X')) throw new FormatException ();
+                       return NumberToString (format, new NumberStore (value), nfi, specifier, precision, custom);
                }
-               public static string NumberToString (string format, NumberStore ns, NumberFormatInfo nfi)
+               public static string NumberToString (string format, NumberStore ns, NumberFormatInfo nfi, char specifier, int precision, bool custom)
                {
                        if (ns.IsNaN) {
                                return nfi.NaNSymbol;
@@ -69,19 +136,9 @@ namespace System
                                        return nfi.NegativeInfinitySymbol;
                        }
 
-                       char specifier;
-                       int precision;
-                       bool custom;
-
-                       if (format == null || format.Length == 0)
-                               format = "G";
-
-                       if (nfi == null)
+                       if (nfi == null) 
                                nfi = NumberFormatInfo.GetInstance (null);
                        
-                       if (!ParseBasicFormat (format, out specifier, out precision, out custom))
-                               throw new FormatException (Locale.GetText ("The specified format '" + format + "' is invalid"));
-                       
                        if (custom){
                                if (ns.IsFloatingSource)
                                        ns.RoundEffectiveDigits (ns.DefaultPrecision);
@@ -103,7 +160,7 @@ namespace System
                                case 'g':
                                case 'G':
                                        if (precision <= 0)
-                                               ns.RoundEffectiveDigits (ns.DefaultPrecision);
+                                               ns.RoundEffectiveDigits (ns.DefaultPrecision, ns.IsBankerApplicable, true);
                                        else
                                                ns.RoundEffectiveDigits (precision);
                                        break;
@@ -135,7 +192,9 @@ namespace System
                                return FormatFixedPoint (ns, precision, nfi);
                        case 'g':
                        case 'G':
-                               return FormatGeneral (ns, precision, nfi, specifier == 'G');
+                               if (ns.IsFloatingSource || ns.IsDecimalSource || precision != -1)
+                                       return FormatGeneral (ns, precision, nfi, specifier == 'G', false);
+                               return FormatDecimal (ns, precision, nfi);
                        case 'n':
                        case 'N':
                                return FormatNumber (ns, precision, nfi);
@@ -145,12 +204,10 @@ namespace System
                        case 'r':
                        case 'R':
                                if (ns.IsFloatingSource) {
-                                       return FormatGeneral (ns, ns.DefaultPrecision, nfi, true);
+                                       return FormatGeneral (ns, ns.DefaultPrecision, nfi, true, true);
                                } else {
                                        throw new FormatException (Locale.GetText ("The specified format cannot be used in this instance"));
                                }
-                       case 'x': 
-                       case 'X': return FormatHexadecimal (ns, precision, nfi, specifier == 'X');
                        default: 
                                throw new FormatException (Locale.GetText ("The specified format '" + format + "' is invalid"));
                        }       
@@ -158,20 +215,22 @@ namespace System
                #endregion
 
                #region BasicParser
-               private static bool ParseBasicFormat (string format, out char specifier, out int precision, out bool custom)
-               {                                
+               private static void ParseBasicFormat (string format, out char specifier, out int precision, out bool custom)
+               {
+                       if (format == null || format.Length == 0) {
+                               precision = -1;
+                               specifier = 'G';
+                               custom = false;
+                               return;
+                       }
+
                        precision = -1;
-                       specifier = '\0';
+                       specifier = format[0];
                        custom = false;
-                       
-                       if (format.Length < 1)
-                               return false;
-                       
-                       specifier = format [0];
 
                        if (Char.IsLetter (specifier)) {
                                if (format.Length == 1)
-                                       return true;
+                                       return;
 
                                bool flag = true;
                                precision = 0;
@@ -190,11 +249,11 @@ namespace System
                                        }
                                }
                                if (flag)
-                                       return true;
+                                       return;
                        }
 
                        custom = true;
-                       return true;
+                       return;
                }       
 
                #endregion
@@ -360,7 +419,7 @@ namespace System
                }
                internal static string FormatDecimal (NumberStore ns, int precision, NumberFormatInfo nfi)
                {
-                       if (ns.IsFloatingSource)
+                       if (ns.IsFloatingSource || ns.IsDecimalSource)
                                throw new FormatException ();
 
                        precision = precision > 0 ? precision : 1;
@@ -398,13 +457,13 @@ namespace System
 
                internal static string FormatGeneral (NumberStore ns)
                {
-                       return FormatGeneral (ns, -1, NumberFormatInfo.CurrentInfo, true);
+                       return FormatGeneral (ns, -1, NumberFormatInfo.CurrentInfo, true, false);
                }
                internal static string FormatGeneral (NumberStore ns, IFormatProvider provider)
                {
-                       return FormatGeneral (ns, -1, NumberFormatInfo.GetInstance (provider), true);
+                       return FormatGeneral (ns, -1, NumberFormatInfo.GetInstance (provider), true, false);
                }
-               private static string FormatGeneral (NumberStore ns, int precision, NumberFormatInfo nfi, bool roundtrip)
+               private static string FormatGeneral (NumberStore ns, int precision, NumberFormatInfo nfi, bool upper, bool roundtrip)
                {
                        if (ns.ZeroOnly)
                                return "0";
@@ -412,7 +471,7 @@ namespace System
                        precision = precision > 0 ? precision : ns.DefaultPrecision;
 
                        int exponent = 0;
-                       bool expMode = (ns.IntegerDigits > precision || ns.DecimalPointPosition <= -4);
+                       bool expMode = (ns.IsDecimalSource && precision == ns.DefaultPrecision ? false : (ns.IntegerDigits > precision || ns.DecimalPointPosition <= -4));
                        if (expMode) {
                                while (!(ns.DecimalPointPosition == 1 && ns.GetChar (0) != '0')) {
                                        if (ns.DecimalPointPosition > 1) {
@@ -425,7 +484,7 @@ namespace System
                                }
                        }
 
-                       precision = precision < ns.DefaultPrecision + 2 ? (precision < 17 ? precision : 17) : ns.DefaultPrecision + 2;
+                       precision = precision < ns.DefaultPrecision + 2 ? (precision < ns.DefaultMaxPrecision ? precision : ns.DefaultMaxPrecision) : ns.DefaultPrecision + 2;
                        StringBuilder cb = new StringBuilder (ns.IntegerDigits + precision + 16);
                        if (expMode) {
                                if (ns.RoundDecimal (precision - 1)) {
@@ -433,7 +492,10 @@ namespace System
                                        exponent ++;
                                }
                        } else if (!roundtrip) {
-                               ns.RoundDecimal (precision);
+                               if (ns.IsDecimalSource)
+                                       ns.RoundPos (precision);
+                               else
+                                       ns.RoundDecimal (precision, true, false);
                        }
 
                        if (!ns.Positive) {
@@ -448,7 +510,7 @@ namespace System
                        }
 
                        if (expMode) {
-                               if (roundtrip)
+                               if (upper)
                                        cb.Append ('E');
                                else
                                        cb.Append ('e');
@@ -583,37 +645,49 @@ namespace System
 
                        return sb.ToString ();
                }
-               internal static string FormatHexadecimal (NumberStore ns, int precision, NumberFormatInfo nfi, bool upper)
+               unsafe static string FormatHexadecimal (ulong value, bool positive, int byteSize, int precision, bool upper)
                {
-                       if (ns.IsFloatingSource)
-                               throw new FormatException ();
-
-                       int intSize = ns.DefaultByteSize;
-                       ulong value = 0;
-                       for (int i = 0; i < ns.IntegerDigits; i++) {
-                               value *= 10;
-                               value += ns.GetDigitByte (i);
-                       }
-
-                       if (!ns.Positive) {
-                               value = (ulong)(Math.Pow (2, intSize * 8)) - value;
+                       if (!positive) {
+                               /* for large values the cast to ulong is going to return 0 anyway (at least on x86, possibly a MS/Mono runtime bug) */
+#if FALSE
+                               if (byteSize < 8) {
+                                       value = (ulong)(Math.Pow (2, byteSize * 8)) - value;
+                               } else {
+                                       value = 0 - value;
+                               }
+#else
+                               switch (byteSize) {
+                               case 1:
+                                       value = (ulong)(256UL - value);
+                                       break;
+                               case 2:
+                                       value = (ulong)(65536UL - value);
+                                       break;
+                               case 4:
+                                       value = (ulong)(4294967296UL - value);
+                                       break;
+                               case 8:
+                                       value = 0 - value;
+                                       break;
+                               }
+#endif
                        }
 
                        char[] digits = (upper ? digitUpperTable : digitLowerTable);
-                       CharBuffer sb = new CharBuffer (16 + precision + 1);
-
+                       int size = precision > 16 ? precision : 16;
+                       char* buffer = stackalloc char [size];
+                       char* last = buffer + size;
+                       char* ptr = last;
+                       
                        while (value > 0) {
-                               sb.InsertToFront (digits [value % 16]);
+                               *--ptr = digits[value & 0xF];
                                value >>= 4;
                        }
 
-                       if (sb.Length == 0)
-                               sb.InsertToFront ('0');
+                       while (ptr == last || last - ptr < precision)
+                               *--ptr = '0';
 
-                       if (sb.Length < precision)
-                               sb.InsertToFront ('0', precision - sb.Length);
-
-                       return sb.ToString ();
+                       return new string (ptr, 0, (int)(last - ptr));
                }
                internal static string FormatExponential (NumberStore ns, int precision, NumberFormatInfo nfi, bool upper)
                {
@@ -693,7 +767,7 @@ namespace System
                                cb.Append (digitLowerTable [exponent / 100 % 10]);
                                cb.Append (digitLowerTable [exponent / 10 % 10]);
                                cb.Append (digitLowerTable [exponent % 10]);
-                       /*} else { // exponent range is 0\81`\81}324
+                       /*} else { // exponent range is 0...+-324
                                int pos = cb.Length;
                                int count = 3;
                                while (exponent > 0 || --count > 0) {
@@ -714,7 +788,7 @@ namespace System
                        int length = 0;
                        CustomInfo.GetActiveSection (format,ref p, ns.ZeroOnly, ref offset, ref length);
                        if (length == 0) {
-                               return ns.Positive ? "" : nfi.NegativeSign;
+                               return ns.Positive ? String.Empty : nfi.NegativeSign;
                        }
                        ns.Positive = p;
 
@@ -798,8 +872,8 @@ namespace System
                                if (info.DecimalDigits <= 0 && info.IntegerDigits <= 0)
                                        ns.Positive = true;
 
-                               /*if (sb_int.Length < info.IntegerDigits)
-                                       sb_int.Insert (0, "0", info.IntegerDigits - sb_int.Length);*/
+                               if (sb_int.Length < info.IntegerDigits)
+                                       sb_int.Insert (0, "0", info.IntegerDigits - sb_int.Length);
 
                                while (sb_exp.Length < info.ExponentDigits - info.ExponentTailSharpDigits)
                                        sb_exp.Insert (0, '0');
@@ -1727,6 +1801,69 @@ namespace System
 
                                return false;
                        }
+
+
+                       public NumberStore (decimal value)
+                       {
+                               int[] bits = decimal.GetBits (value);
+                               _positive = (bits [3] & 0x80000000) == 0;
+                               bits[3] = bits [3] & 0x7FFFFFFF;
+                               int ss = (bits [3] & 0x1F0000) >> 16;
+                               ulong lo = (ulong)((((ulong)bits[1]) << 32) | (uint)bits [0]);
+                               ulong hi = (uint)bits [2];
+                               uint rest = 0;
+
+                               int digits = 0;
+                               while (hi > 0 || lo > 0) {
+                                       digits ++;
+                                       DivideDecimal (ref lo, ref hi, 10, ref rest);
+                               }
+
+                               lo = (ulong)((((ulong)bits[1]) << 32) | (uint)bits [0]);
+                               hi = (uint)bits [2];
+
+                               _digits = new byte [digits];
+                               int i = digits;
+                               while (hi > 0 || lo > 0) {
+                                       DivideDecimal (ref lo, ref hi, 10, ref rest);
+                                       _digits [--i] = (byte)rest;
+                               }
+
+                               _infinity = _NaN = false;
+                               _decPointPos = _digits.Length - ss;
+                               _defPrecision = _defMaxPrecision = 100;
+                               _defByteSize = 16;
+                       }
+                       static int DivideDecimal (ref ulong lo, ref ulong hi, uint factor, ref uint rest)
+                       {
+                               ulong a, b, c, h;
+
+                               h = hi;
+                               a = (uint)(h >> 32);
+                               b = a / factor;
+                               a -= b * factor;
+                               a <<= 32;
+                               a |= (uint) h;
+                               c = a / factor;
+                               a -= c * factor;
+                               a <<= 32;
+                               hi = b << 32 | (uint)c;
+
+                               h = lo;
+                               a |= (uint)(h >> 32);
+                               b = a / factor;
+                               a -= b * factor;
+                               a <<= 32;
+                               a |= (uint) h;
+                               c = a / factor;
+                               a -= c * factor;
+                               lo = b << 32 | (uint)c;
+
+                               rest = (uint)a;
+
+                               a <<= 1;
+                               return (a >= factor || (a == factor && (c & 1) == 1)) ? 1 : 0;
+                       }
                        #endregion
 
                        #region Public Property
@@ -1765,6 +1902,16 @@ namespace System
                        public bool IsFloatingSource {
                                get { return _defPrecision == 15 || _defPrecision == 7; }
                        }
+                       public bool IsDecimalSource {
+                               get { return _defPrecision > 30; }
+                       }
+                       public bool IsBankerApplicable {
+                               get {
+                                       if ((_digits == null) || (_digits.Length < 2))
+                                               return false;
+                                       return ((_digits [_digits.Length - 2] & 1) == 1);
+                               }
+                       }
                        public bool ZeroOnly {
                                get {
                                        for (int i = 0; i < _digits.Length; i++)
@@ -1823,13 +1970,14 @@ namespace System
                        }
                        public bool RoundDecimal (int decimals)
                        {
-                               return RoundDecimal (decimals, true);
+                               return RoundDecimal (decimals, true, true);
                        }
-                       public bool RoundDecimal (int decimals, bool carryFive)
+                       public bool RoundDecimal (int decimals, bool carryFive, bool countZero)
                        {
                                bool carry = false;
 
-                               decimals += _decPointPos;
+                               if (countZero || (_decPointPos > 0))
+                                       decimals += _decPointPos;
 
                                if (!HasDecimal || decimals >= _digits.Length)
                                        return false;
@@ -1891,6 +2039,33 @@ namespace System
                                if (digits + 1 < _digits.Length && _digits [digits + 1] == 5 && _digits [digits] % 2 == (carryEven ? 0 : 1))
                                        carryFive = false;
 
+                               /// are we cutting from the maximum precision ?
+                               if (_digits.Length == _defMaxPrecision) {
+                                       // special case if we *aren't* cutting inside the extra precision (e.g. 16 on 17)
+                                       if (digits != _defMaxPrecision - 1) {
+                                               // ok, here we look at the *two* extra numbers we're keeping
+                                               // (we keep 17 digits while the true precision is 15 digits).
+                                               int extra = _digits[_defMaxPrecision - 2] * 10 + _digits[_defMaxPrecision - 1];
+                                               carry = (extra >= 50);
+                                               if (carry) {
+                                                       _digits[_defMaxPrecision - 2] = 0;
+                                                       _digits[_defMaxPrecision - 1] = 0;
+                                                       int d = _digits.Length - 3;
+                                                       CarryPropagation (ref d, carryFive, ref carry);
+                                               }
+                                       }
+                               }
+                               CarryPropagation (ref digits, carryFive, ref carry);
+
+                               for (int i = digits; i < _digits.Length; i++)
+                                       _digits [i] = 0;
+                               TrimDecimalEndZeros ();
+
+                               return carry;
+                       }
+
+                       private void CarryPropagation (ref int digits, bool carryFive, ref bool carry)
+                       {
                                for (int i = digits; i >= 0; i--) {
                                        RoundHelper (i, carryFive, ref carry);
                                        if (!carry)
@@ -1898,20 +2073,15 @@ namespace System
                                }
 
                                if (carry) {
-                                       byte[] temp = new byte [_digits.Length + 1];
+                                       byte[] temp = new byte[_digits.Length + 1];
                                        _digits.CopyTo (temp, 1);
-                                       temp [0] = 1;
+                                       temp[0] = 1;
                                        _digits = temp;
-                                       _decPointPos ++;
-                                       digits ++;
+                                       _decPointPos++;
+                                       digits++;
                                }
-
-                               for (int i = digits; i < _digits.Length; i++)
-                                       _digits [i] = 0;
-                               TrimDecimalEndZeros ();
-
-                               return carry;
                        }
+
                        #endregion
 
                        #region Trim
@@ -2210,66 +2380,6 @@ namespace System
                        }
                        #endregion
                }
-               internal struct CharBuffer
-               {
-                       int offset;
-                       char[] buffer;
-
-                       public CharBuffer (int capacity)
-                       {
-                               buffer = new char [capacity];
-                               offset = capacity;
-                       }
-
-                       void AllocateBuffer (int size)
-                       {
-                               size = size > buffer.Length * 2 ? size : buffer.Length * 2;
-                               char[] newBuffer = new char [size];
-                               offset += size - buffer.Length;
-                               Array.Copy (buffer, 0, newBuffer, size - buffer.Length, buffer.Length);
-                               buffer = newBuffer;
-                       }
-
-                       void CheckInsert (int length)
-                       {
-                               if (offset - length < 0) {
-                                       AllocateBuffer (buffer.Length + length - offset);
-                               }
-                       }
-
-                       public void InsertToFront (char c)
-                       {
-                               CheckInsert (1);
-                               buffer [--offset] = c;
-                       }
-
-                       public void InsertToFront (char c, int repeat)
-                       {
-                               CheckInsert (repeat);
-                               while (repeat-- > 0) {
-                                       buffer [--offset] = c;
-                               }
-                       }
-
-                       public char this [int index] 
-                       {
-                               get {
-                                       return buffer [offset + index];
-                               }
-                       }
-
-                       public override string ToString()
-                       {
-                               if (offset == buffer.Length)
-                                       return "";
-                               
-                               return new string (buffer, offset, buffer.Length - offset);
-                       }
-
-                       public int Length {
-                               get { return buffer.Length - offset; }
-                       }
-               }
                #endregion
        }
-}
\ No newline at end of file
+}