2 // System.NumberFormatter.cs
5 // Kazuki Oikawa (kazuki@panicode.com)
6 // Eyal Alaluf (eyala@mainsoft.com)
9 // NumberFormatter is shared with Grasshopper and hence the #if TARGET_JVM
10 // The differentiating issues are:
11 // * Mono runs faster when NumberFormatter is a struct. Grasshopper (and .Net
12 // for that matter) run faster when its a class.
13 // * No support for unsafe code in Grasshopper.
19 using System.Globalization;
21 using System.Runtime.CompilerServices;
26 internal partial struct NumberFormatter
28 internal sealed partial class NumberFormatter
33 const int DefaultExpPrecision = 6;
34 const int HundredMillion = 100000000;
35 const long SeventeenDigitsThreshold = 10000000000000000;
36 const ulong ULongDivHundredMillion = UInt64.MaxValue / HundredMillion;
37 const ulong ULongModHundredMillion = 1 + UInt64.MaxValue % HundredMillion;
39 const int DoubleBitsExponentShift = 52;
40 const int DoubleBitsExponentMask = 0x7ff;
41 const long DoubleBitsMantissaMask = 0xfffffffffffff;
42 const int DecimalBitsScaleMask = 0x1f0000;
44 const int SingleDefPrecision = 7;
45 const int DoubleDefPrecision = 15;
46 const int Int8DefPrecision = 3;
47 const int UInt8DefPrecision = 3;
48 const int Int16DefPrecision = 5;
49 const int UInt16DefPrecision = 5;
50 const int Int32DefPrecision = 10;
51 const int UInt32DefPrecision = 10;
52 const int Int64DefPrecision = 19;
53 const int UInt64DefPrecision = 20;
54 const int DecimalDefPrecision = 100;
55 const int TenPowersListLength = 19;
58 // The below arrays are taken from mono/metatdata/number-formatter.h
60 private static readonly unsafe ulong* MantissaBitsTable;
61 private static readonly unsafe int* TensExponentTable;
62 private static readonly unsafe char* DigitLowerTable;
63 private static readonly unsafe char* DigitUpperTable;
64 private static readonly unsafe long* TenPowersList;
66 // DecHexDigits s a translation table from a decimal number to its
67 // digits hexadecimal representation (e.g. DecHexDigits [34] = 0x34).
68 private static readonly unsafe int* DecHexDigits;
70 [MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.InternalCall)]
71 private unsafe static extern void GetFormatterTables (out ulong* MantissaBitsTable, out int* TensExponentTable,
72 out char* DigitLowerTable, out char* DigitUpperTable,
73 out long* TenPowersList, out int* DecHexDigits);
75 unsafe static NumberFormatter()
77 GetFormatterTables (out MantissaBitsTable, out TensExponentTable,
78 out DigitLowerTable, out DigitUpperTable, out TenPowersList, out DecHexDigits);
83 static long GetTenPowerOf(int i)
85 return TenPowersList [i];
87 #endregion Static Fields
91 private readonly bool _NaN;
92 private readonly bool _infinity;
93 private readonly bool _isCustomFormat;
94 private readonly bool _specifierIsUpper;
95 private readonly char _specifier;
96 private readonly sbyte _precision;
97 private readonly sbyte _defPrecision;
99 private bool _positive;
100 private sbyte _digitsLen;
101 private sbyte _offset; // Represent the first digit offset.
102 private int _decPointPos;
104 // The following fields are a hexadeimal representation of the digits.
105 // For instance _val = 0x234 represents the digits '2', '3', '4'.
106 private uint _val1; // Digits 0 - 7.
107 private uint _val2; // Digits 8 - 15.
108 private uint _val3; // Digits 16 - 23.
109 private uint _val4; // Digits 23 - 31. Only needed for decimals.
113 #region Constructor Helpers
115 // Translate an unsigned int to hexadecimal digits.
116 // i.e. 123456789 is represented by _val1 = 0x23456789 and _val2 = 0x1
117 private void InitDecHexDigits (uint value)
119 if (value >= HundredMillion) {
120 uint div1 = value / HundredMillion;
121 value = value - HundredMillion * div1;
122 _val2 = ToDecHex ((int)div1);
124 _val1 = ToDecHex ((int)value);
127 // Translate an unsigned long to hexadecimal digits.
128 private void InitDecHexDigits (ulong value)
130 if (value >= HundredMillion) {
131 long div1 = (long)(value / HundredMillion);
132 value = value - HundredMillion * (ulong)div1;
133 if (div1 >= HundredMillion) {
134 int div2 = (int)(div1 / HundredMillion);
135 div1 = div1 - div2 * (long)HundredMillion;
136 _val3 = ToDecHex (div2);
139 _val2 = ToDecHex ((int)(div1));
142 _val1 = ToDecHex ((int)value);
145 // Translate a decimal integer to hexadecimal digits.
146 // The decimal integer is 96 digits and its value is hi * 2^64 + lo.
147 // is the lower 64 bits.
148 private void InitDecHexDigits (uint hi, ulong lo)
151 InitDecHexDigits (lo); // Only the lower 64 bits matter.
155 // Compute (hi, lo) = (hi , lo) / HundredMillion.
156 uint divhi = hi / HundredMillion;
157 ulong remhi = hi - divhi * HundredMillion;
158 ulong divlo = lo / HundredMillion;
159 ulong remlo = lo - divlo * HundredMillion + remhi * ULongModHundredMillion;
161 lo = divlo + remhi * ULongDivHundredMillion;
162 divlo = remlo / HundredMillion;
163 remlo -= divlo * HundredMillion;
165 _val1 = ToDecHex ((int)remlo);
167 // Divide hi * 2 ^ 64 + lo by HundredMillion using the fact that
168 // hi < HundredMillion.
169 divlo = lo / HundredMillion;
170 remlo = lo - divlo * HundredMillion;
173 lo += hi * ULongDivHundredMillion;
174 remlo += hi * ULongModHundredMillion;
175 divlo = remlo / HundredMillion;
177 remlo -= divlo * HundredMillion;
179 _val2 = ToDecHex ((int)remlo);
181 // Now we are left with 64 bits store in lo.
182 if (lo >= HundredMillion) {
183 divlo = lo / HundredMillion;
184 lo -= divlo * HundredMillion;
185 _val4 = ToDecHex ((int)divlo);
187 _val3 = ToDecHex ((int)lo);
190 // Helper to translate an int in the range 0 .. 9999 to its
191 // Hexadecimal digits representation.
195 private static int FastToDecHex (int val)
199 // Uses 2^19 (524288) to compute val / 100 for val < 10000.
200 int v = (val * 5243) >> 19;
202 res = DecHexDigits [v] << 8;
204 return res | DecHexDigits [val];
207 // Helper to translate an int in the range 0 .. 99999999 to its
208 // Hexadecimal digits representation.
209 private static uint ToDecHex (int val)
215 res = FastToDecHex (v) << 16;
217 return (uint)(res | FastToDecHex (val));
220 // Helper to count number of hexadecimal digits in a number.
221 private static int FastDecHexLen (int val)
228 else if (val < 0x1000)
234 private static int DecHexLen (uint val)
240 return FastDecHexLen (v);
241 return 4 + FastDecHexLen (v >> 16);
244 // Count number of hexadecimal digits stored in _val1 .. _val4.
245 private sbyte DecHexLen ()
248 return (sbyte)(DecHexLen (_val4) + 24);
250 return (sbyte)(DecHexLen (_val3) + 16);
252 return (sbyte)(DecHexLen (_val2) + 8);
254 return (sbyte)DecHexLen (_val1);
258 // Helper to count the 10th scale (number of digits) in a number
259 private static int ScaleOrder (long hi)
261 for (int i = TenPowersListLength - 1; i >= 0; i--)
262 if (hi >= GetTenPowerOf (i))
267 // Compute the initial precision for rounding a floating number
268 // according to the used format.
269 int InitialFloatingPrecision ()
271 if (_specifier == 'R')
272 return _defPrecision + 2;
273 if (_precision < _defPrecision)
274 return _defPrecision;
275 if (_specifier == 'G')
276 return Math.Min (_defPrecision + 2, _precision);
277 if (_specifier == 'E')
278 return Math.Min (_defPrecision + 2, _precision + 1);
279 return _defPrecision;
282 // Parse the given format and extract the precision in it.
283 // Returns -1 for empty formats and -2 to indicate that the format
284 // is a custom format.
285 private static int ParsePrecision (string format)
288 for (int i = 1; i < format.Length; i++) {
289 int val = format [i] - '0';
290 precision = precision * 10 + val;
291 if (val < 0 || val > 9 || precision > 99)
297 #endregion Constructor Helpers
301 // Parse the given format and initialize the following fields:
302 // _isCustomFormat, _specifierIsUpper, _specifier & _precision.
303 private NumberFormatter (string format)
306 // Init all fields when using a struct.
307 _val1 = _val2 = _val3 = _val4 = 0;
310 _decPointPos = _digitsLen = 0;
311 _positive = _NaN = _infinity = false;
315 _isCustomFormat = false;
316 _specifierIsUpper = true;
318 if (format == null || format.Length == 0) {
323 char specifier = format [0];
324 if (specifier >= 'a' && specifier <= 'z') {
325 specifier = (char)(specifier - 'a' + 'A');
326 _specifierIsUpper = false;
328 else if (specifier < 'A' || specifier > 'Z') {
329 _isCustomFormat = true;
333 _specifier = specifier;
334 if (format.Length > 1) {
335 _precision = (sbyte)ParsePrecision (format);
336 if (_precision == -2) { // Is it a custom format?
337 _isCustomFormat = true;
344 public NumberFormatter (string format, sbyte value)
345 : this (format, (int)value)
347 _defPrecision = Int8DefPrecision;
348 if (_specifier == 'X' && value < 0) {
350 _decPointPos = _digitsLen = 2;
355 public NumberFormatter (string format, byte value)
356 : this (format, (uint)value)
358 _defPrecision = UInt8DefPrecision;
361 public NumberFormatter (string format, short value)
362 : this (format, (int)value)
364 _defPrecision = Int16DefPrecision;
365 if (_specifier == 'X' && value < 0) {
366 _val1 = (ushort)value;
367 _decPointPos = _digitsLen = 4;
372 public NumberFormatter (string format, ushort value)
373 : this (format, (uint)value)
375 _defPrecision = UInt16DefPrecision;
378 public NumberFormatter (string format, int value)
381 _defPrecision = Int32DefPrecision;
382 _infinity = _NaN = false;
383 _positive = value >= 0;
389 if (_specifier == 'X') {
391 _decPointPos = _digitsLen = DecHexLen ();
398 InitDecHexDigits ((uint)value);
399 _decPointPos = _digitsLen = DecHexLen ();
402 public NumberFormatter (string format, uint value)
405 _infinity = _NaN = false;
406 _defPrecision = UInt32DefPrecision;
413 if (_specifier == 'X') {
415 _decPointPos = _digitsLen = DecHexLen ();
419 InitDecHexDigits (value);
420 _decPointPos = _digitsLen = DecHexLen ();
423 public NumberFormatter (string format, long value)
426 _infinity = _NaN = false;
427 _defPrecision = Int64DefPrecision;
428 _positive = value >= 0;
434 if (_specifier == 'X') {
436 _val2 = (uint)(value >> 32);
437 _decPointPos = _digitsLen = DecHexLen ();
444 InitDecHexDigits ((ulong)value);
445 _decPointPos = _digitsLen = DecHexLen ();
448 public NumberFormatter (string format, ulong value)
451 _infinity = _NaN = false;
452 _defPrecision = UInt64DefPrecision;
459 if (_specifier == 'X') {
461 _val2 = (uint)(value >> 32);
462 _decPointPos = _digitsLen = DecHexLen ();
466 InitDecHexDigits (value);
467 _decPointPos = _digitsLen = DecHexLen ();
470 public NumberFormatter (string format, float value)
471 : this (format, value, SingleDefPrecision)
475 public NumberFormatter (string format, double value)
476 : this (format, value, DoubleDefPrecision)
480 #if UNSAFE_TABLES // No unsafe code under TARGET_JVM
483 public NumberFormatter (string format, double value, sbyte defPrecision)
486 _defPrecision = defPrecision;
489 long bits = BitConverter.DoubleToInt64Bits (value);
490 _positive = bits >= 0;
491 bits &= Int64.MaxValue;
498 int e = (int)(bits >> DoubleBitsExponentShift);
499 long m = bits & DoubleBitsMantissaMask;
500 if (e == DoubleBitsExponentMask) {
508 // We need 'm' to be large enough so we won't lose precision.
510 int scale = ScaleOrder (m);
511 if (scale < DoubleDefPrecision) {
512 expAdjust = scale - DoubleDefPrecision;
513 m *= GetTenPowerOf (-expAdjust);
517 m = (m + DoubleBitsMantissaMask + 1) * 10;
521 // multiply the mantissa by 10 ^ N
523 ulong hi = (ulong)m >> 32;
524 ulong lo2 = MantissaBitsTable [e];
525 ulong hi2 = lo2 >> 32;
527 ulong mm = hi * lo2 + lo * hi2 + ((lo * lo2) >> 32);
528 long res = (long)(hi * hi2 + (mm >> 32));
529 while (res < SeventeenDigitsThreshold) {
530 mm = (mm & UInt32.MaxValue) * 10;
531 res = res * 10 + (long)(mm >> 32);
534 if ((mm & 0x80000000) != 0)
537 int order = DoubleDefPrecision + 2;
538 _decPointPos = TensExponentTable [e] + expAdjust + order;
540 // Rescale 'res' to the initial precision (15-17 for doubles).
541 int initialPrecision = InitialFloatingPrecision ();
542 if (order > initialPrecision) {
543 long val = GetTenPowerOf (order - initialPrecision);
544 res = (res + (val >> 1)) / val;
545 order = initialPrecision;
547 if (res >= GetTenPowerOf (order)) {
552 InitDecHexDigits ((ulong)res);
553 _offset = (sbyte)CountTrailingZeros ();
554 _digitsLen = (sbyte)(order - _offset);
557 public NumberFormatter (string format, decimal value)
560 _infinity = _NaN = false;
561 _defPrecision = DecimalDefPrecision;
563 int[] bits = decimal.GetBits (value);
564 int scale = (bits [3] & DecimalBitsScaleMask) >> 16;
565 _positive = bits [3] >= 0;
566 if (bits [0] == 0 && bits [1] == 0 && bits [2] == 0) {
567 _decPointPos = -scale;
572 InitDecHexDigits ((uint)bits [2], ((ulong)bits [1] << 32) | (uint)bits [0]);
573 _digitsLen = DecHexLen ();
574 _decPointPos = _digitsLen - scale;
577 #endregion Constructors
579 #region Inner String Buffer
581 private char[] _cbuf;
584 private void ResetCharBuf (int size)
586 if (_cbuf == null || _cbuf.Length < size)
587 _cbuf = new char [size];
591 private void Resize (int len)
593 char[] newBuf = new char [len];
594 Array.Copy (_cbuf, newBuf, _ind);
598 private void Append (char c)
600 if (_ind == _cbuf.Length)
605 private void Append (char c, int cnt)
607 if (_ind + cnt > _cbuf.Length)
608 Resize (_ind + cnt + 10);
613 private void Append (string s)
616 if (_ind + slen > _cbuf.Length)
617 Resize (_ind + slen + 10);
618 for (int i = 0; i < slen; i++)
619 _cbuf [_ind++] = s [i];
622 #endregion Inner String Buffer
624 #region Helper properties
626 private int IntegerDigits {
627 get { return _decPointPos > 0 ? _decPointPos : 1; }
630 private int DecimalDigits {
631 get { return _digitsLen > _decPointPos ? _digitsLen - _decPointPos : 0; }
634 private bool IsIntegerSource {
635 get { return _defPrecision < 30 && _defPrecision != DoubleDefPrecision && _defPrecision != SingleDefPrecision; }
638 private bool IsFloatingSource {
639 get { return _defPrecision == DoubleDefPrecision || _defPrecision == SingleDefPrecision; }
642 private bool IsDecimalSource {
643 get { return _defPrecision > 30; }
646 private bool IsZero {
647 get { return _digitsLen == 0; }
650 private bool IsZeroInteger {
651 get { return _digitsLen == 0 || _decPointPos <= 0; }
654 #endregion Helper properties
658 private void RoundPos (int pos)
660 RoundBits (_digitsLen - pos);
663 private bool RoundDecimal (int decimals)
665 return RoundBits (_digitsLen - _decPointPos - decimals);
668 private bool RoundBits (int shift)
671 if (IsDecimalSource) {
672 _digitsLen += _offset;
673 RemoveTrailingZeros ();
678 if (shift > _digitsLen) {
681 _val1 = _val2 = _val3 = _val4 = 0;
686 _digitsLen += _offset;
695 shift = (shift - 1) << 2;
696 uint v = _val1 >> shift;
697 uint rem16 = v & 0xf;
698 _val1 = (v ^ rem16) << shift;
701 _val1 |= 0x99999999 >> (28 - shift);
703 sbyte newlen = DecHexLen ();
704 res = newlen != _digitsLen;
705 _decPointPos = _decPointPos + newlen - _digitsLen;
708 RemoveTrailingZeros ();
712 private void RemoveTrailingZeros ()
714 _offset = (sbyte)CountTrailingZeros ();
715 _digitsLen -= _offset;
716 if (_digitsLen == 0) {
723 private void AddOneToDecHex ()
725 if (_val1 == 0x99999999) {
727 if (_val2 == 0x99999999) {
729 if (_val3 == 0x99999999) {
731 _val4 = AddOneToDecHex (_val4);
734 _val3 = AddOneToDecHex (_val3);
737 _val2 = AddOneToDecHex (_val2);
740 _val1 = AddOneToDecHex (_val1);
743 // Assume val != 0x99999999
744 private static uint AddOneToDecHex (uint val)
746 if ((val & 0xffff) == 0x9999)
747 if ((val & 0xffffff) == 0x999999)
748 if ((val & 0xfffffff) == 0x9999999)
749 return val + 0x06666667;
751 return val + 0x00666667;
752 else if ((val & 0xfffff) == 0x99999)
753 return val + 0x00066667;
755 return val + 0x00006667;
756 else if ((val & 0xff) == 0x99)
757 if ((val & 0xfff) == 0x999)
758 return val + 0x00000667;
760 return val + 0x00000067;
761 else if ((val & 0xf) == 0x9)
762 return val + 0x00000007;
767 private int CountTrailingZeros ()
770 return CountTrailingZeros (_val1);
772 return CountTrailingZeros (_val2) + 8;
774 return CountTrailingZeros (_val3) + 16;
776 return CountTrailingZeros (_val4) + 24;
780 private static int CountTrailingZeros (uint val)
782 if ((val & 0xffff) == 0)
783 if ((val & 0xffffff) == 0)
784 if ((val & 0xfffffff) == 0)
788 else if ((val & 0xfffff) == 0)
792 else if ((val & 0xff) == 0)
793 if ((val & 0xfff) == 0)
797 else if ((val & 0xf) == 0)
805 #region public number formatting methods
807 public static string NumberToString (string format, sbyte value, NumberFormatInfo nfi)
809 return new NumberFormatter (format, value).NumberToString (format, nfi);
812 public static string NumberToString (string format, byte value, NumberFormatInfo nfi)
814 return new NumberFormatter (format, value).NumberToString (format, nfi);
817 public static string NumberToString (string format, ushort value, NumberFormatInfo nfi)
819 return new NumberFormatter (format, value).NumberToString (format, nfi);
822 public static string NumberToString (string format, short value, NumberFormatInfo nfi)
824 return new NumberFormatter (format, value).NumberToString (format, nfi);
827 public static string NumberToString (string format, uint value, NumberFormatInfo nfi)
829 return new NumberFormatter (format, value).NumberToString (format, nfi);
832 public static string NumberToString (string format, int value, NumberFormatInfo nfi)
834 return new NumberFormatter (format, value).NumberToString (format, nfi);
837 public static string NumberToString (string format, ulong value, NumberFormatInfo nfi)
839 return new NumberFormatter (format, value).NumberToString (format, nfi);
842 public static string NumberToString (string format, long value, NumberFormatInfo nfi)
844 return new NumberFormatter (format, value).NumberToString (format, nfi);
847 public static string NumberToString (string format, float value, NumberFormatInfo nfi)
849 NumberFormatter rep = new NumberFormatter (format, value);
850 if (rep._specifier == 'R')
851 return rep.FormatRoundtrip (value, nfi);
852 return rep.NumberToString (format, nfi);
855 public static string NumberToString (string format, double value, NumberFormatInfo nfi)
857 NumberFormatter rep = new NumberFormatter (format, value);
858 if (rep._specifier == 'R')
859 return rep.FormatRoundtrip (value, nfi);
860 return rep.NumberToString (format, nfi);
863 public static string NumberToString (string format, decimal value, NumberFormatInfo nfi)
865 return new NumberFormatter (format, value).NumberToString (format, nfi);
868 public string NumberToString (string format, NumberFormatInfo nfi)
870 if (IsFloatingSource) {
872 return nfi.NaNSymbol;
876 return nfi.PositiveInfinitySymbol;
878 return nfi.NegativeInfinitySymbol;
882 switch (_specifier) {
884 return FormatCurrency (_precision, nfi);
887 return FormatDecimal (_precision, nfi);
888 throw new FormatException ();
890 return FormatExponential (_precision, nfi);
892 return FormatFixedPoint (_precision, nfi);
894 if (IsIntegerSource && _precision <= 0)
895 return FormatDecimal (-1, nfi);
896 return FormatGeneral (_precision, nfi);
898 return FormatNumber (_precision, nfi);
900 return FormatPercent (_precision, nfi);
903 return FormatHexadecimal (_precision);
904 throw new FormatException ("The specified format cannot be used in this instance");
906 throw new FormatException ("The specified format cannot be used in this instance");
909 return FormatCustom (format, nfi);
910 throw new FormatException ("The specified format '" + format + "' is invalid");
914 public string FormatCurrency (int precision, NumberFormatInfo nfi)
916 precision = (precision >= 0 ? precision : nfi.CurrencyDecimalDigits);
917 RoundDecimal (precision);
918 ResetCharBuf (IntegerDigits * 2 + precision * 2 + 16);
921 switch (nfi.CurrencyPositivePattern) {
923 Append (nfi.CurrencySymbol);
926 Append (nfi.CurrencySymbol);
932 switch (nfi.CurrencyNegativePattern) {
935 Append (nfi.CurrencySymbol);
938 Append (nfi.NegativeSign);
939 Append (nfi.CurrencySymbol);
942 Append (nfi.CurrencySymbol);
943 Append (nfi.NegativeSign);
946 Append (nfi.CurrencySymbol);
952 Append (nfi.NegativeSign);
955 Append (nfi.NegativeSign);
958 Append (nfi.NegativeSign);
959 Append (nfi.CurrencySymbol);
963 Append (nfi.CurrencySymbol);
967 Append (nfi.CurrencySymbol);
969 Append (nfi.NegativeSign);
973 Append (nfi.CurrencySymbol);
982 AppendIntegerStringWithGroupSeparator (nfi.RawCurrencyGroupSizes, nfi.CurrencyGroupSeparator);
985 Append (nfi.CurrencyDecimalSeparator);
986 AppendDecimalString (precision);
990 switch (nfi.CurrencyPositivePattern) {
992 Append (nfi.CurrencySymbol);
996 Append (nfi.CurrencySymbol);
1001 switch (nfi.CurrencyNegativePattern) {
1006 Append (nfi.NegativeSign);
1009 Append (nfi.CurrencySymbol);
1013 Append (nfi.CurrencySymbol);
1016 Append (nfi.NegativeSign);
1017 Append (nfi.CurrencySymbol);
1020 Append (nfi.CurrencySymbol);
1021 Append (nfi.NegativeSign);
1025 Append (nfi.CurrencySymbol);
1029 Append (nfi.CurrencySymbol);
1030 Append (nfi.NegativeSign);
1033 Append (nfi.NegativeSign);
1036 Append (nfi.NegativeSign);
1038 Append (nfi.CurrencySymbol);
1045 Append (nfi.CurrencySymbol);
1051 return new string (_cbuf, 0, _ind);
1054 public string FormatDecimal (int precision, NumberFormatInfo nfi)
1056 if (precision < _digitsLen)
1057 precision = _digitsLen;
1061 ResetCharBuf (precision + 1);
1064 nfi = NumberFormatInfo.GetInstance (null);
1066 Append (nfi.NegativeSign);
1068 AppendDigits (0, precision);
1070 return new string (_cbuf, 0, _ind);
1073 #if UNSAFE_TABLES // No unsafe code under TARGET_JVM
1076 public string FormatHexadecimal (int precision)
1078 int size = Math.Max (precision, _decPointPos);
1080 char* digits = _specifierIsUpper ? DigitUpperTable : DigitLowerTable;
1082 char[] digits = _specifierIsUpper ? DigitUpperTable : DigitLowerTable;
1084 ResetCharBuf (size);
1086 ulong val = _val1 | ((ulong)_val2 << 32);
1088 _cbuf [--size] = digits [val & 0xf];
1091 return new string (_cbuf, 0, _ind);
1094 public string FormatFixedPoint (int precision, NumberFormatInfo nfi)
1096 if (precision == -1)
1097 precision = nfi.NumberDecimalDigits;
1099 RoundDecimal (precision);
1101 ResetCharBuf (IntegerDigits + precision + 2);
1104 Append (nfi.NegativeSign);
1106 AppendIntegerString (IntegerDigits);
1108 if (precision > 0) {
1109 Append (nfi.NumberDecimalSeparator);
1110 AppendDecimalString (precision);
1113 return new string (_cbuf, 0, _ind);
1116 public string FormatRoundtrip (double origval, NumberFormatInfo nfi)
1118 NumberFormatter nfc = GetClone ();
1119 string shortRep = FormatGeneral (_defPrecision, nfi);
1120 // Check roundtrip only for "normal" double values.
1121 if (!_NaN && !_infinity && origval == Double.Parse (shortRep, nfi))
1123 return nfc.FormatGeneral (_defPrecision + 2, nfi);
1126 public string FormatRoundtrip (float origval, NumberFormatInfo nfi)
1128 NumberFormatter nfc = GetClone ();
1129 string shortRep = FormatGeneral (_defPrecision, nfi);
1130 // Check roundtrip only for "normal" double values.
1131 if (!_NaN && !_infinity && origval == Single.Parse (shortRep, nfi))
1133 return nfc.FormatGeneral (_defPrecision + 2, nfi);
1136 public string FormatGeneral (int precision, NumberFormatInfo nfi)
1139 if (precision == -1) {
1140 enableExp = IsFloatingSource;
1141 precision = _defPrecision;
1146 precision = _defPrecision;
1147 RoundPos (precision);
1150 int intDigits = _decPointPos;
1151 int digits = _digitsLen;
1152 int decDigits = digits - intDigits;
1154 if ((intDigits > precision || intDigits <= -4) && enableExp)
1155 return FormatExponential (digits - 1, nfi, 2);
1161 ResetCharBuf (decDigits + intDigits + 3);
1164 Append (nfi.NegativeSign);
1169 AppendDigits (digits - intDigits, digits);
1171 if (decDigits > 0) {
1172 Append (nfi.NumberDecimalSeparator);
1173 AppendDigits (0, decDigits);
1176 return new string (_cbuf, 0, _ind);
1179 public string FormatNumber (int precision, NumberFormatInfo nfi)
1181 precision = (precision >= 0 ? precision : nfi.NumberDecimalDigits);
1182 ResetCharBuf (IntegerDigits * 3 + precision);
1183 RoundDecimal (precision);
1186 switch (nfi.NumberNegativePattern) {
1191 Append (nfi.NegativeSign);
1194 Append (nfi.NegativeSign);
1200 AppendIntegerStringWithGroupSeparator (nfi.RawNumberGroupSizes, nfi.NumberGroupSeparator);
1202 if (precision > 0) {
1203 Append (nfi.NumberDecimalSeparator);
1204 AppendDecimalString (precision);
1208 switch (nfi.NumberNegativePattern) {
1213 Append (nfi.NegativeSign);
1217 Append (nfi.NegativeSign);
1222 return new string (_cbuf, 0, _ind);
1225 public string FormatPercent (int precision, NumberFormatInfo nfi)
1227 precision = (precision >= 0 ? precision : nfi.PercentDecimalDigits);
1229 RoundDecimal (precision);
1230 ResetCharBuf (IntegerDigits * 2 + precision + 16);
1233 if (nfi.PercentPositivePattern == 2)
1234 Append (nfi.PercentSymbol);
1237 switch (nfi.PercentNegativePattern) {
1239 Append (nfi.NegativeSign);
1242 Append (nfi.NegativeSign);
1245 Append (nfi.NegativeSign);
1246 Append (nfi.PercentSymbol);
1251 AppendIntegerStringWithGroupSeparator (nfi.RawPercentGroupSizes, nfi.PercentGroupSeparator);
1253 if (precision > 0) {
1254 Append (nfi.PercentDecimalSeparator);
1255 AppendDecimalString (precision);
1259 switch (nfi.PercentPositivePattern) {
1262 Append (nfi.PercentSymbol);
1265 Append (nfi.PercentSymbol);
1270 switch (nfi.PercentNegativePattern) {
1273 Append (nfi.PercentSymbol);
1276 Append (nfi.PercentSymbol);
1281 return new string (_cbuf, 0, _ind);
1284 public string FormatExponential (int precision, NumberFormatInfo nfi)
1286 if (precision == -1)
1287 precision = DefaultExpPrecision;
1289 RoundPos (precision + 1);
1290 return FormatExponential (precision, nfi, 3);
1293 private string FormatExponential (int precision, NumberFormatInfo nfi, int expDigits)
1295 int decDigits = _decPointPos;
1296 int digits = _digitsLen;
1297 int exponent = decDigits - 1;
1298 decDigits = _decPointPos = 1;
1300 ResetCharBuf (precision + 8);
1303 Append (nfi.NegativeSign);
1305 AppendOneDigit (digits - 1);
1307 if (precision > 0) {
1308 Append (nfi.NumberDecimalSeparator);
1309 AppendDigits (digits - precision - 1, digits - _decPointPos);
1312 AppendExponent (nfi, exponent, expDigits);
1314 return new string (_cbuf, 0, _ind);
1317 public string FormatCustom (string format, NumberFormatInfo nfi)
1322 CustomInfo.GetActiveSection (format, ref p, IsZero, ref offset, ref length);
1324 return _positive ? string.Empty : nfi.NegativeSign;
1327 CustomInfo info = CustomInfo.Parse (format, offset, length, nfi);
1329 Console.WriteLine ("Format : {0}",format);
1330 Console.WriteLine ("DecimalDigits : {0}",info.DecimalDigits);
1331 Console.WriteLine ("DecimalPointPos : {0}",info.DecimalPointPos);
1332 Console.WriteLine ("DecimalTailSharpDigits : {0}",info.DecimalTailSharpDigits);
1333 Console.WriteLine ("IntegerDigits : {0}",info.IntegerDigits);
1334 Console.WriteLine ("IntegerHeadSharpDigits : {0}",info.IntegerHeadSharpDigits);
1335 Console.WriteLine ("IntegerHeadPos : {0}",info.IntegerHeadPos);
1336 Console.WriteLine ("UseExponent : {0}",info.UseExponent);
1337 Console.WriteLine ("ExponentDigits : {0}",info.ExponentDigits);
1338 Console.WriteLine ("ExponentTailSharpDigits : {0}",info.ExponentTailSharpDigits);
1339 Console.WriteLine ("ExponentNegativeSignOnly : {0}",info.ExponentNegativeSignOnly);
1340 Console.WriteLine ("DividePlaces : {0}",info.DividePlaces);
1341 Console.WriteLine ("Percents : {0}",info.Percents);
1342 Console.WriteLine ("Permilles : {0}",info.Permilles);
1344 StringBuilder sb_int = new StringBuilder (info.IntegerDigits * 2);
1345 StringBuilder sb_dec = new StringBuilder (info.DecimalDigits * 2);
1346 StringBuilder sb_exp = (info.UseExponent ? new StringBuilder (info.ExponentDigits * 2) : null);
1349 if (info.Percents > 0)
1350 Multiply10(2 * info.Percents);
1351 if (info.Permilles > 0)
1352 Multiply10(3 * info.Permilles);
1353 if (info.DividePlaces > 0)
1354 Divide10(info.DividePlaces);
1356 bool expPositive = true;
1357 if (info.UseExponent && (info.DecimalDigits > 0 || info.IntegerDigits > 0)) {
1359 RoundPos (info.DecimalDigits + info.IntegerDigits);
1360 diff -= _decPointPos - info.IntegerDigits;
1361 _decPointPos = info.IntegerDigits;
1364 expPositive = diff <= 0;
1365 AppendNonNegativeNumber (sb_exp, diff < 0 ? -diff : diff);
1368 RoundDecimal (info.DecimalDigits);
1370 if (info.IntegerDigits != 0 || !IsZeroInteger)
1371 AppendIntegerString (IntegerDigits, sb_int);
1373 AppendDecimalString (DecimalDigits, sb_dec);
1375 if (info.UseExponent) {
1376 if (info.DecimalDigits <= 0 && info.IntegerDigits <= 0)
1379 if (sb_int.Length < info.IntegerDigits)
1380 sb_int.Insert (0, "0", info.IntegerDigits - sb_int.Length);
1382 while (sb_exp.Length < info.ExponentDigits - info.ExponentTailSharpDigits)
1383 sb_exp.Insert (0, '0');
1385 if (expPositive && !info.ExponentNegativeSignOnly)
1386 sb_exp.Insert (0, nfi.PositiveSign);
1387 else if (!expPositive)
1388 sb_exp.Insert (0, nfi.NegativeSign);
1391 if (sb_int.Length < info.IntegerDigits - info.IntegerHeadSharpDigits)
1392 sb_int.Insert (0, "0", info.IntegerDigits - info.IntegerHeadSharpDigits - sb_int.Length);
1393 if (info.IntegerDigits == info.IntegerHeadSharpDigits && IsZeroOnly (sb_int))
1394 sb_int.Remove (0, sb_int.Length);
1397 ZeroTrimEnd (sb_dec, true);
1398 while (sb_dec.Length < info.DecimalDigits - info.DecimalTailSharpDigits)
1399 sb_dec.Append ('0');
1400 if (sb_dec.Length > info.DecimalDigits)
1401 sb_dec.Remove (info.DecimalDigits, sb_dec.Length - info.DecimalDigits);
1403 return info.Format (format, offset, length, nfi, _positive, sb_int, sb_dec, sb_exp);
1405 #endregion public number formatting methods
1407 #region StringBuilder formatting helpers
1409 private static void ZeroTrimEnd (StringBuilder sb)
1411 ZeroTrimEnd (sb, false);
1414 private static void ZeroTrimEnd (StringBuilder sb, bool canEmpty)
1417 for (int i = sb.Length - 1; (canEmpty ? i >= 0 : i > 0); i--) {
1424 sb.Remove (sb.Length - len, len);
1427 private static bool IsZeroOnly (StringBuilder sb)
1429 for (int i = 0; i < sb.Length; i++)
1430 if (char.IsDigit (sb [i]) && sb [i] != '0')
1435 private static void AppendNonNegativeNumber (StringBuilder sb, int v)
1438 throw new ArgumentException ();
1440 int i = ScaleOrder (v) - 1;
1442 int n = v / (int)GetTenPowerOf (i);
1443 sb.Append ((char)('0' | n));
1444 v -= (int)GetTenPowerOf (i--) * n;
1448 #endregion StringBuilder formatting helpers
1450 #region Append helpers
1452 private void AppendIntegerString (int minLength, StringBuilder sb)
1454 if (_decPointPos <= 0) {
1455 sb.Append ('0', minLength);
1459 if (_decPointPos < minLength)
1460 sb.Append ('0', minLength - _decPointPos);
1462 AppendDigits (_digitsLen - _decPointPos, _digitsLen, sb);
1465 private void AppendIntegerString (int minLength)
1467 if (_decPointPos <= 0) {
1468 Append ('0', minLength);
1472 if (_decPointPos < minLength)
1473 Append ('0', minLength - _decPointPos);
1475 AppendDigits (_digitsLen - _decPointPos, _digitsLen);
1478 private void AppendDecimalString (int precision, StringBuilder sb)
1480 AppendDigits (_digitsLen - precision - _decPointPos, _digitsLen - _decPointPos, sb);
1483 private void AppendDecimalString (int precision)
1485 AppendDigits (_digitsLen - precision - _decPointPos, _digitsLen - _decPointPos);
1488 private void AppendIntegerStringWithGroupSeparator (int[] groups, string groupSeparator)
1490 if (IsZeroInteger) {
1497 for (int i = 0; i < groups.Length; i++) {
1498 total += groups [i];
1499 if (total <= _decPointPos)
1505 if (groups.Length > 0 && total > 0) {
1507 int groupSize = groups [groupIndex];
1508 int fraction = _decPointPos > total ? _decPointPos - total : 0;
1509 if (groupSize == 0) {
1510 while (groupIndex >= 0 && groups [groupIndex] == 0)
1513 groupSize = fraction > 0 ? fraction : groups [groupIndex];
1516 counter = groupSize;
1518 groupIndex += fraction / groupSize;
1519 counter = fraction % groupSize;
1521 counter = groupSize;
1526 for (int i = 0; ;) {
1527 if ((_decPointPos - i) <= counter || counter == 0) {
1528 AppendDigits (_digitsLen - _decPointPos, _digitsLen - i);
1531 AppendDigits (_digitsLen - i - counter, _digitsLen - i);
1533 Append (groupSeparator);
1534 if (--groupIndex < groups.Length && groupIndex >= 0)
1535 groupSize = groups [groupIndex];
1536 counter = groupSize;
1540 AppendDigits (_digitsLen - _decPointPos, _digitsLen);
1544 // minDigits is in the range 1..3
1545 private void AppendExponent (NumberFormatInfo nfi, int exponent, int minDigits)
1547 if (_specifierIsUpper || _specifier == 'R')
1553 Append (nfi.PositiveSign);
1555 Append (nfi.NegativeSign);
1556 exponent = -exponent;
1560 Append ('0', minDigits);
1561 else if (exponent < 10) {
1562 Append ('0', minDigits - 1);
1563 Append ((char)('0' | exponent));
1566 int hexDigit = FastToDecHex (exponent);
1567 if (exponent >= 100 || minDigits == 3)
1568 Append ((char)('0' | (hexDigit >> 8)));
1569 Append ((char)('0' | ((hexDigit >> 4) & 0xf)));
1570 Append ((char)('0' | (hexDigit & 0xf)));
1574 private void AppendOneDigit (int start)
1576 if (_ind == _cbuf.Length)
1585 else if (start < 16)
1587 else if (start < 24)
1589 else if (start < 32)
1593 v >>= (start & 0x7) << 2;
1594 _cbuf [_ind++] = (char)('0' | v & 0xf);
1597 private void AppendDigits (int start, int end)
1602 int i = _ind + (end - start);
1603 if (i > _cbuf.Length)
1610 for (int next = start + 8 - (start & 0x7); ; start = next, next += 8) {
1614 else if (next == 16)
1616 else if (next == 24)
1618 else if (next == 32)
1622 v >>= (start & 0x7) << 2;
1626 _cbuf [--i] = (char)('0' | v & 0xf);
1627 switch (next - start) {
1629 _cbuf [--i] = (char)('0' | (v >>= 4) & 0xf);
1632 _cbuf [--i] = (char)('0' | (v >>= 4) & 0xf);
1635 _cbuf [--i] = (char)('0' | (v >>= 4) & 0xf);
1638 _cbuf [--i] = (char)('0' | (v >>= 4) & 0xf);
1641 _cbuf [--i] = (char)('0' | (v >>= 4) & 0xf);
1644 _cbuf [--i] = (char)('0' | (v >>= 4) & 0xf);
1647 _cbuf [--i] = (char)('0' | (v >>= 4) & 0xf);
1657 private void AppendDigits (int start, int end, StringBuilder sb)
1662 int i = sb.Length + (end - start);
1668 for (int next = start + 8 - (start & 0x7); ; start = next, next += 8) {
1672 else if (next == 16)
1674 else if (next == 24)
1676 else if (next == 32)
1680 v >>= (start & 0x7) << 2;
1683 sb [--i] = (char)('0' | v & 0xf);
1684 switch (next - start) {
1686 sb [--i] = (char)('0' | (v >>= 4) & 0xf);
1689 sb [--i] = (char)('0' | (v >>= 4) & 0xf);
1692 sb [--i] = (char)('0' | (v >>= 4) & 0xf);
1695 sb [--i] = (char)('0' | (v >>= 4) & 0xf);
1698 sb [--i] = (char)('0' | (v >>= 4) & 0xf);
1701 sb [--i] = (char)('0' | (v >>= 4) & 0xf);
1704 sb [--i] = (char)('0' | (v >>= 4) & 0xf);
1714 #endregion Append helpers
1718 private void Multiply10(int count)
1720 if (count <= 0 || _digitsLen == 0)
1723 _decPointPos += count;
1726 private void Divide10(int count)
1728 if (count <= 0 || _digitsLen == 0)
1731 _decPointPos -= count;
1734 private NumberFormatter GetClone ()
1736 return (NumberFormatter)this.MemberwiseClone ();
1743 private class CustomInfo
1745 public bool UseGroup = false;
1746 public int DecimalDigits = 0;
1747 public int DecimalPointPos = -1;
1748 public int DecimalTailSharpDigits = 0;
1749 public int IntegerDigits = 0;
1750 public int IntegerHeadSharpDigits = 0;
1751 public int IntegerHeadPos = 0;
1752 public bool UseExponent = false;
1753 public int ExponentDigits = 0;
1754 public int ExponentTailSharpDigits = 0;
1755 public bool ExponentNegativeSignOnly = true;
1756 public int DividePlaces = 0;
1757 public int Percents = 0;
1758 public int Permilles = 0;
1760 public static void GetActiveSection (string format, ref bool positive, bool zero, ref int offset, ref int length)
1762 int[] lens = new int [3];
1765 char literal = '\0';
1766 for (int i = 0; i < format.Length; i++) {
1767 char c = format [i];
1769 if (c == literal || (literal == '\0' && (c == '\"' || c == '\''))) {
1770 if (literal == '\0')
1777 if (literal == '\0' && format [i] == ';' && (i == 0 || format [i - 1] != '\\')) {
1778 lens [index++] = i - lastPos;
1787 length = format.Length;
1791 if (positive || zero) {
1796 if (lens [0] + 1 < format.Length) {
1798 offset = lens [0] + 1;
1799 length = format.Length - offset;
1810 offset = lens [0] + lens [1] + 2;
1811 length = format.Length - offset;
1821 offset = lens [0] + 1;
1833 offset = lens [0] + lens [1] + 2;
1844 offset = lens [0] + 1;
1855 throw new ArgumentException ();
1858 public static CustomInfo Parse (string format, int offset, int length, NumberFormatInfo nfi)
1860 char literal = '\0';
1861 bool integerArea = true;
1862 bool decimalArea = false;
1863 bool exponentArea = false;
1864 bool sharpContinues = true;
1866 CustomInfo info = new CustomInfo ();
1867 int groupSeparatorCounter = 0;
1869 for (int i = offset; i - offset < length; i++) {
1870 char c = format [i];
1872 if (c == literal && c != '\0') {
1876 if (literal != '\0')
1879 if (exponentArea && (c != '\0' && c != '0' && c != '#')) {
1880 exponentArea = false;
1881 integerArea = (info.DecimalPointPos < 0);
1882 decimalArea = !integerArea;
1893 if (c == '\"' || c == '\'') {
1898 if (sharpContinues && integerArea)
1899 info.IntegerHeadSharpDigits++;
1900 else if (decimalArea)
1901 info.DecimalTailSharpDigits++;
1902 else if (exponentArea)
1903 info.ExponentTailSharpDigits++;
1908 sharpContinues = false;
1910 info.DecimalTailSharpDigits = 0;
1911 else if (exponentArea)
1912 info.ExponentTailSharpDigits = 0;
1914 if (info.IntegerHeadPos == -1)
1915 info.IntegerHeadPos = i;
1918 info.IntegerDigits++;
1919 if (groupSeparatorCounter > 0)
1920 info.UseGroup = true;
1921 groupSeparatorCounter = 0;
1923 else if (decimalArea)
1924 info.DecimalDigits++;
1925 else if (exponentArea)
1926 info.ExponentDigits++;
1930 if (info.UseExponent)
1933 info.UseExponent = true;
1934 integerArea = false;
1935 decimalArea = false;
1936 exponentArea = true;
1937 if (i + 1 - offset < length) {
1938 char nc = format [i + 1];
1940 info.ExponentNegativeSignOnly = false;
1941 if (nc == '+' || nc == '-')
1943 else if (nc != '0' && nc != '#') {
1944 info.UseExponent = false;
1945 if (info.DecimalPointPos < 0)
1952 integerArea = false;
1954 exponentArea = false;
1955 if (info.DecimalPointPos == -1)
1956 info.DecimalPointPos = i;
1965 if (integerArea && info.IntegerDigits > 0)
1966 groupSeparatorCounter++;
1973 if (info.ExponentDigits == 0)
1974 info.UseExponent = false;
1976 info.IntegerHeadSharpDigits = 0;
1978 if (info.DecimalDigits == 0)
1979 info.DecimalPointPos = -1;
1981 info.DividePlaces += groupSeparatorCounter * 3;
1986 public string Format (string format, int offset, int length, NumberFormatInfo nfi, bool positive, StringBuilder sb_int, StringBuilder sb_dec, StringBuilder sb_exp)
1988 StringBuilder sb = new StringBuilder ();
1989 char literal = '\0';
1990 bool integerArea = true;
1991 bool decimalArea = false;
1992 int intSharpCounter = 0;
1993 int sb_int_index = 0;
1994 int sb_dec_index = 0;
1996 int[] groups = nfi.RawNumberGroupSizes;
1997 string groupSeparator = nfi.NumberGroupSeparator;
1998 int intLen = 0, total = 0, groupIndex = 0, counter = 0, groupSize = 0;
1999 if (UseGroup && groups.Length > 0) {
2000 intLen = sb_int.Length;
2001 for (int i = 0; i < groups.Length; i++) {
2002 total += groups [i];
2003 if (total <= intLen)
2006 groupSize = groups [groupIndex];
2007 int fraction = intLen > total ? intLen - total : 0;
2008 if (groupSize == 0) {
2009 while (groupIndex >= 0 && groups [groupIndex] == 0)
2012 groupSize = fraction > 0 ? fraction : groups [groupIndex];
2015 counter = groupSize;
2017 groupIndex += fraction / groupSize;
2018 counter = fraction % groupSize;
2020 counter = groupSize;
2028 for (int i = offset; i - offset < length; i++) {
2029 char c = format [i];
2031 if (c == literal && c != '\0') {
2035 if (literal != '\0') {
2043 if (i - offset < length)
2044 sb.Append (format [i]);
2048 if (c == '\"' || c == '\'')
2056 if (IntegerDigits - intSharpCounter < sb_int.Length + sb_int_index || c == '0')
2057 while (IntegerDigits - intSharpCounter + sb_int_index < sb_int.Length) {
2058 sb.Append (sb_int [sb_int_index++]);
2059 if (UseGroup && --intLen > 0 && --counter == 0) {
2060 sb.Append (groupSeparator);
2061 if (--groupIndex < groups.Length && groupIndex >= 0)
2062 groupSize = groups [groupIndex];
2063 counter = groupSize;
2068 else if (decimalArea) {
2069 if (sb_dec_index < sb_dec.Length)
2070 sb.Append (sb_dec [sb_dec_index++]);
2078 if (sb_exp == null || !UseExponent) {
2087 for (q = i + 1; q - offset < length; q++) {
2088 if (format [q] == '0') {
2092 if (q == i + 1 && (format [q] == '+' || format [q] == '-'))
2101 integerArea = (DecimalPointPos < 0);
2102 decimalArea = !integerArea;
2113 if (DecimalPointPos == i) {
2114 if (DecimalDigits > 0) {
2115 while (sb_int_index < sb_int.Length)
2116 sb.Append (sb_int [sb_int_index++]);
2118 if (sb_dec.Length > 0)
2119 sb.Append (nfi.NumberDecimalSeparator);
2121 integerArea = false;
2127 sb.Append (nfi.PercentSymbol);
2130 sb.Append (nfi.PerMilleSymbol);
2139 sb.Insert (0, nfi.NegativeSign);
2141 return sb.ToString ();