Redesign System/NumberFormatter to improve primitive numeric types ToString performance.
[mono.git] / mcs / class / corlib / System / NumberFormatter.cs
1 //
2 // System.NumberFormatter.cs
3 //
4 // Author:
5 //   Kazuki Oikawa (kazuki@panicode.com)
6 //   Eyal Alaluf (eyala@mainsoft.com)
7 //
8
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.
14 #if !TARGET_JVM
15 #define STRUCT
16 #define UNSAFE_TABLES
17 #endif
18
19 using System.Globalization;
20 using System.Text;
21 using System.Runtime.CompilerServices;
22
23 namespace System
24 {
25 #if STRUCT
26         internal partial struct NumberFormatter
27 #else
28         internal sealed partial class NumberFormatter
29 #endif
30         {
31                 #region Static Fields
32
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;
38
39                 const int DoubleBitsExponentShift = 52;
40                 const int DoubleBitsExponentMask = 0x7ff;
41                 const long DoubleBitsMantissaMask = 0xfffffffffffff;
42                 const int DecimalBitsScaleMask = 0x1f0000;
43
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;
56
57 #if UNSAFE_TABLES
58                 // The below arrays are taken from mono/metatdata/number-formatter.h
59
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;
65
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;
69
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);
74
75                 unsafe static NumberFormatter()
76                 {
77                         GetFormatterTables (out MantissaBitsTable, out TensExponentTable,
78                                 out DigitLowerTable, out DigitUpperTable, out TenPowersList, out DecHexDigits);
79                 }
80
81                 unsafe
82 #endif
83                 static long GetTenPowerOf(int i)
84                 {
85                         return TenPowersList [i];
86                 }
87                 #endregion Static Fields
88
89                 #region Fields
90
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;
98
99                 private bool _positive;
100                 private sbyte _digitsLen;
101                 private sbyte _offset; // Represent the first digit offset.
102                 private int _decPointPos;
103
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.
110
111                 #endregion Fields
112
113                 #region Constructor Helpers
114
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)
118                 {
119                         if (value >= HundredMillion) {
120                                 uint div1 = value / HundredMillion;
121                                 value = value - HundredMillion * div1;
122                                 _val2 = ToDecHex ((int)div1);
123                         }
124                         _val1 = ToDecHex ((int)value);
125                 }
126
127                 // Translate an unsigned long to hexadecimal digits.
128                 private void InitDecHexDigits (ulong value)
129                 {
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);
137                                 }
138                                 if (div1 != 0)
139                                         _val2 = ToDecHex ((int)(div1));
140                         }
141                         if (value != 0)
142                                 _val1 = ToDecHex ((int)value);
143                 }
144
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)
149                 {
150                         if (hi == 0) {
151                                 InitDecHexDigits (lo); // Only the lower 64 bits matter.
152                                 return;
153                         }
154
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;
160                         hi = divhi;
161                         lo = divlo + remhi * ULongDivHundredMillion;
162                         divlo = remlo / HundredMillion;
163                         remlo -= divlo * HundredMillion;
164                         lo += divlo;
165                         _val1 = ToDecHex ((int)remlo);
166
167                         // Divide hi * 2 ^ 64 + lo by HundredMillion using the fact that
168                         // hi < HundredMillion.
169                         divlo = lo / HundredMillion;
170                         remlo = lo - divlo * HundredMillion;
171                         lo = divlo;
172                         if (hi != 0) {
173                                 lo += hi * ULongDivHundredMillion;
174                                 remlo += hi * ULongModHundredMillion;
175                                 divlo = remlo / HundredMillion;
176                                 lo += divlo;
177                                 remlo -= divlo * HundredMillion;
178                         }
179                         _val2 = ToDecHex ((int)remlo);
180
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);
186                         }
187                         _val3 = ToDecHex ((int)lo);
188                 }
189
190                 // Helper to translate an int in the range 0 .. 9999 to its
191                 // Hexadecimal digits representation.
192 #if UNSAFE_TABLES
193                 unsafe
194 #endif
195                 private static int FastToDecHex (int val)
196                 {
197                         int res = 0;
198                         if (val >= 100) {
199                                 // Uses 2^19 (524288) to compute val / 100 for val < 10000.
200                                 int v = (val * 5243) >> 19;
201                                 val -= v * 100;
202                                 res = DecHexDigits [v] << 8;
203                         }
204                         return res | DecHexDigits [val];
205                 }
206
207                 // Helper to translate an int in the range 0 .. 99999999 to its
208                 // Hexadecimal digits representation.
209                 private static uint ToDecHex (int val)
210                 {
211                         int res = 0;
212                         if (val >= 10000) {
213                                 int v = val / 10000;
214                                 val -= v * 10000;
215                                 res = FastToDecHex (v) << 16;
216                         }
217                         return (uint)(res | FastToDecHex (val));
218                 }
219
220                 // Helper to count number of hexadecimal digits in a number.
221                 private static int FastDecHexLen (int val)
222                 {
223                         if (val < 0x100)
224                                 if (val < 0x10)
225                                         return 1;
226                                 else
227                                         return 2;
228                         else if (val < 0x1000)
229                                 return 3;
230                         else
231                                 return 4;
232                 }
233
234                 private static int DecHexLen (uint val)
235                 {
236                         int v = (int)val;
237                         if (v < 0)
238                                 return 8;
239                         if (v < 0x10000)
240                                 return FastDecHexLen (v);
241                         return 4 + FastDecHexLen (v >> 16);
242                 }
243
244                 // Count number of hexadecimal digits stored in _val1 .. _val4.
245                 private sbyte DecHexLen ()
246                 {
247                         if (_val4 != 0)
248                                 return (sbyte)(DecHexLen (_val4) + 24);
249                         if (_val3 != 0)
250                                 return (sbyte)(DecHexLen (_val3) + 16);
251                         if (_val2 != 0)
252                                 return (sbyte)(DecHexLen (_val2) + 8);
253                         if (_val1 != 0)
254                                 return (sbyte)DecHexLen (_val1);
255                         return 0;
256                 }
257
258                 // Helper to count the 10th scale (number of digits) in a number
259                 private static int ScaleOrder (long hi)
260                 {
261                         for (int i = TenPowersListLength - 1; i >= 0; i--)
262                                 if (hi >= GetTenPowerOf (i))
263                                         return i + 1;
264                         return 1;
265                 }
266
267                 // Compute the initial precision for rounding a floating number
268                 // according to the used format.
269                 int InitialFloatingPrecision ()
270                 {
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;
280                 }
281
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)
286                 {
287                         int precision = 0;
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)
292                                         return -2;
293                         }
294                         return precision;
295                 }
296
297                 #endregion Constructor Helpers
298
299                 #region Constructors
300
301                 // Parse the given format and initialize the following fields:
302                 //   _isCustomFormat, _specifierIsUpper, _specifier & _precision.
303                 private NumberFormatter (string format)
304                 {
305 #if STRUCT
306                         // Init all fields when using a struct.
307                         _val1 = _val2 = _val3 = _val4 = 0;
308                         _defPrecision = 0;
309                         _offset = 0;
310                         _decPointPos = _digitsLen = 0;
311                         _positive = _NaN = _infinity = false;
312                         _cbuf = null;
313                         _ind = 0;
314 #endif
315                         _isCustomFormat = false;
316                         _specifierIsUpper = true;
317                         _precision = -1;
318                         if (format == null || format.Length == 0) {
319                                 _specifier = 'G';
320                                 return;
321                         }
322
323                         char specifier = format [0];
324                         if (specifier >= 'a' && specifier <= 'z') {
325                                 specifier = (char)(specifier - 'a' + 'A');
326                                 _specifierIsUpper = false;
327                         }
328                         else if (specifier < 'A' || specifier > 'Z') {
329                                 _isCustomFormat = true;
330                                 _specifier = '0';
331                                 return;
332                         }
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;
338                                         _specifier = '0';
339                                         _precision = -1;
340                                 }
341                         }
342                 }
343
344                 public NumberFormatter (string format, sbyte value)
345                         : this (format, (int)value)
346                 {
347                         _defPrecision = Int8DefPrecision;
348                         if (_specifier == 'X' && value < 0) {
349                                 _val1 = (byte)value;
350                                 _decPointPos = _digitsLen = 2;
351                                 return;
352                         }
353                 }
354
355                 public NumberFormatter (string format, byte value)
356                         : this (format, (uint)value)
357                 {
358                         _defPrecision = UInt8DefPrecision;
359                 }
360
361                 public NumberFormatter (string format, short value)
362                         : this (format, (int)value)
363                 {
364                         _defPrecision = Int16DefPrecision;
365                         if (_specifier == 'X' && value < 0) {
366                                 _val1 = (ushort)value;
367                                 _decPointPos = _digitsLen = 4;
368                                 return;
369                         }
370                 }
371
372                 public NumberFormatter (string format, ushort value)
373                         : this (format, (uint)value)
374                 {
375                         _defPrecision = UInt16DefPrecision;
376                 }
377
378                 public NumberFormatter (string format, int value)
379                         : this (format)
380                 {
381                         _defPrecision = Int32DefPrecision;
382                         _infinity = _NaN = false;
383                         _positive = value >= 0;
384
385                         if (value == 0) {
386                                 _decPointPos = 1;
387                                 return;
388                         }
389                         if (_specifier == 'X') {
390                                 _val1 = (uint)value;
391                                 _decPointPos = _digitsLen = DecHexLen ();
392                                 return;
393                         }
394
395                         if (value < 0)
396                                 value = -value;
397
398                         InitDecHexDigits ((uint)value);
399                         _decPointPos = _digitsLen = DecHexLen ();
400                 }
401
402                 public NumberFormatter (string format, uint value)
403                         : this (format)
404                 {
405                         _infinity = _NaN = false;
406                         _defPrecision = UInt32DefPrecision;
407                         _positive = true;
408
409                         if (value == 0) {
410                                 _decPointPos = 1;
411                                 return;
412                         }
413                         if (_specifier == 'X') {
414                                 _val1 = value;
415                                 _decPointPos = _digitsLen = DecHexLen ();
416                                 return;
417                         }
418
419                         InitDecHexDigits (value);
420                         _decPointPos = _digitsLen = DecHexLen ();
421                 }
422
423                 public NumberFormatter (string format, long value)
424                         : this (format)
425                 {
426                         _infinity = _NaN = false;
427                         _defPrecision = Int64DefPrecision;
428                         _positive = value >= 0;
429
430                         if (value == 0) {
431                                 _decPointPos = 1;
432                                 return;
433                         }
434                         if (_specifier == 'X') {
435                                 _val1 = (uint)value;
436                                 _val2 = (uint)(value >> 32);
437                                 _decPointPos = _digitsLen = DecHexLen ();
438                                 return;
439                         }
440
441                         if (value < 0)
442                                 value = -value;
443
444                         InitDecHexDigits ((ulong)value);
445                         _decPointPos = _digitsLen = DecHexLen ();
446                 }
447
448                 public NumberFormatter (string format, ulong value)
449                         : this (format)
450                 {
451                         _infinity = _NaN = false;
452                         _defPrecision = UInt64DefPrecision;
453                         _positive = true;
454
455                         if (value == 0) {
456                                 _decPointPos = 1;
457                                 return;
458                         }
459                         if (_specifier == 'X') {
460                                 _val1 = (uint)value;
461                                 _val2 = (uint)(value >> 32);
462                                 _decPointPos = _digitsLen = DecHexLen ();
463                                 return;
464                         }
465
466                         InitDecHexDigits (value);
467                         _decPointPos = _digitsLen = DecHexLen ();
468                 }
469
470                 public NumberFormatter (string format, float value)
471                         : this (format, value, SingleDefPrecision)
472                 {
473                 }
474
475                 public NumberFormatter (string format, double value)
476                         : this (format, value, DoubleDefPrecision)
477                 {
478                 }
479
480 #if UNSAFE_TABLES // No unsafe code under TARGET_JVM
481                 unsafe
482 #endif
483                 public NumberFormatter (string format, double value, sbyte defPrecision)
484                         : this (format)
485                 {
486                         _defPrecision = defPrecision;
487
488                         // Double to bits
489                         long bits = BitConverter.DoubleToInt64Bits (value);
490                         _positive = bits >= 0;
491                         bits &= Int64.MaxValue;
492                         if (bits == 0) {
493                                 _decPointPos = 1;
494                                 _positive = true;
495                                 return;
496                         }
497
498                         int e = (int)(bits >> DoubleBitsExponentShift);
499                         long m = bits & DoubleBitsMantissaMask;
500                         if (e == DoubleBitsExponentMask) {
501                                 _NaN = m != 0;
502                                 _infinity = m == 0;
503                                 return;
504                         }
505
506                         int expAdjust = 0;
507                         if (e == 0) {
508                                 // We need 'm' to be large enough so we won't lose precision.
509                                 e = 1;
510                                 int scale = ScaleOrder (m);
511                                 if (scale < DoubleDefPrecision) {
512                                         expAdjust = scale - DoubleDefPrecision;
513                                         m *= GetTenPowerOf (-expAdjust);
514                                 }
515                         }
516                         else {
517                                 m = (m + DoubleBitsMantissaMask + 1) * 10;
518                                 expAdjust = -1;
519                         }
520
521                         // multiply the mantissa by 10 ^ N
522                         ulong lo = (uint)m;
523                         ulong hi = (ulong)m >> 32;
524                         ulong lo2 = MantissaBitsTable [e];
525                         ulong hi2 = lo2 >> 32;
526                         lo2 = (uint)lo2;
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);
532                                 expAdjust--;
533                         }
534                         if ((mm & 0x80000000) != 0)
535                                 res++;
536
537                         int order = DoubleDefPrecision + 2;
538                         _decPointPos = TensExponentTable [e] + expAdjust + order;
539
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;
546                         }
547                         if (res >= GetTenPowerOf (order)) {
548                                 order++;
549                                 _decPointPos++;
550                         }
551
552                         InitDecHexDigits ((ulong)res);
553                         _offset = (sbyte)CountTrailingZeros ();
554                         _digitsLen = (sbyte)(order - _offset);
555                 }
556
557                 public NumberFormatter (string format, decimal value)
558                         : this (format)
559                 {
560                         _infinity = _NaN = false;
561                         _defPrecision = DecimalDefPrecision;
562
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;
568                                 _positive = true;
569                                 return;
570                         }
571
572                         InitDecHexDigits ((uint)bits [2], ((ulong)bits [1] << 32) | (uint)bits [0]);
573                         _digitsLen = DecHexLen ();
574                         _decPointPos = _digitsLen - scale;
575                 }
576
577                 #endregion Constructors
578
579                 #region Inner String Buffer
580
581                 private char[] _cbuf;
582                 private int _ind;
583
584                 private void ResetCharBuf (int size)
585                 {
586                         if (_cbuf == null || _cbuf.Length < size)
587                                 _cbuf = new char [size];
588                         _ind = 0;
589                 }
590
591                 private void Resize (int len)
592                 {
593                         char[] newBuf = new char [len];
594                         Array.Copy (_cbuf, newBuf, _ind);
595                         _cbuf = newBuf;
596                 }
597
598                 private void Append (char c)
599                 {
600                         if (_ind == _cbuf.Length)
601                                 Resize (_ind + 10);
602                         _cbuf [_ind++] = c;
603                 }
604
605                 private void Append (char c, int cnt)
606                 {
607                         if (_ind + cnt > _cbuf.Length)
608                                 Resize (_ind + cnt + 10);
609                         while (cnt-- > 0)
610                                 _cbuf [_ind++] = c;
611                 }
612
613                 private void Append (string s)
614                 {
615                         int slen = s.Length;
616                         if (_ind + slen > _cbuf.Length)
617                                 Resize (_ind + slen + 10);
618                         for (int i = 0; i < slen; i++)
619                                 _cbuf [_ind++] = s [i];
620                 }
621
622                 #endregion Inner String Buffer
623
624                 #region Helper properties
625
626                 private int IntegerDigits {
627                         get { return _decPointPos > 0 ? _decPointPos : 1; }
628                 }
629
630                 private int DecimalDigits {
631                         get { return _digitsLen > _decPointPos ? _digitsLen - _decPointPos : 0; }
632                 }
633
634                 private bool IsIntegerSource {
635                         get { return _defPrecision < 30 && _defPrecision != DoubleDefPrecision && _defPrecision != SingleDefPrecision; }
636                 }
637
638                 private bool IsFloatingSource {
639                         get { return _defPrecision == DoubleDefPrecision || _defPrecision == SingleDefPrecision; }
640                 }
641
642                 private bool IsDecimalSource {
643                         get { return _defPrecision > 30; }
644                 }
645
646                 private bool IsZero {
647                         get { return _digitsLen == 0; }
648                 }
649
650                 private bool IsZeroInteger {
651                         get { return _digitsLen == 0 || _decPointPos <= 0; }
652                 }
653
654                 #endregion Helper properties
655
656                 #region Round
657
658                 private void RoundPos (int pos)
659                 {
660                         RoundBits (_digitsLen - pos);
661                 }
662
663                 private bool RoundDecimal (int decimals)
664                 {
665                         return RoundBits (_digitsLen - _decPointPos - decimals);
666                 }
667
668                 private bool RoundBits (int shift)
669                 {
670                         if (shift <= 0) {
671                                 if (IsDecimalSource) {
672                                         _digitsLen += _offset;
673                                         RemoveTrailingZeros ();
674                                 }
675                                 return false;
676                         }
677
678                         if (shift > _digitsLen) {
679                                 _digitsLen = 0;
680                                 _decPointPos = 1;
681                                 _val1 = _val2 = _val3 = _val4 = 0;
682                                 _positive = true;
683                                 return false;
684                         }
685                         shift += _offset;
686                         _digitsLen += _offset;
687                         while (shift > 8) {
688                                 _val1 = _val2;
689                                 _val2 = _val3;
690                                 _val3 = _val4;
691                                 _val4 = 0;
692                                 _digitsLen -= 8;
693                                 shift -= 8;
694                         }
695                         shift = (shift - 1) << 2;
696                         uint v = _val1 >> shift;
697                         uint rem16 = v & 0xf;
698                         _val1 = (v ^ rem16) << shift;
699                         bool res = false;
700                         if (rem16 >= 0x5) {
701                                 _val1 |= 0x99999999 >> (28 - shift);
702                                 AddOneToDecHex ();
703                                 sbyte newlen = DecHexLen ();
704                                 res = newlen != _digitsLen;
705                                 _decPointPos = _decPointPos + newlen - _digitsLen;
706                                 _digitsLen = newlen;
707                         }
708                         RemoveTrailingZeros ();
709                         return res;
710                 }
711
712                 private void RemoveTrailingZeros ()
713                 {
714                         _offset = (sbyte)CountTrailingZeros ();
715                         _digitsLen -= _offset;
716                         if (_digitsLen == 0) {
717                                 _offset = 0;
718                                 _decPointPos = 1;
719                                 _positive = true;
720                         }
721                 }
722
723                 private void AddOneToDecHex ()
724                 {
725                         if (_val1 == 0x99999999) {
726                                 _val1 = 0;
727                                 if (_val2 == 0x99999999) {
728                                         _val2 = 0;
729                                         if (_val3 == 0x99999999) {
730                                                 _val3 = 0;
731                                                 _val4 = AddOneToDecHex (_val4);
732                                         }
733                                         else
734                                                 _val3 = AddOneToDecHex (_val3);
735                                 }
736                                 else
737                                         _val2 = AddOneToDecHex (_val2);
738                         }
739                         else
740                                 _val1 = AddOneToDecHex (_val1);
741                 }
742
743                 // Assume val != 0x99999999
744                 private static uint AddOneToDecHex (uint val)
745                 {
746                         if ((val & 0xffff) == 0x9999)
747                                 if ((val & 0xffffff) == 0x999999)
748                                         if ((val & 0xfffffff) == 0x9999999)
749                                                 return val + 0x06666667;
750                                         else
751                                                 return val + 0x00666667;
752                                 else if ((val & 0xfffff) == 0x99999)
753                                         return val + 0x00066667;
754                                 else
755                                         return val + 0x00006667;
756                         else if ((val & 0xff) == 0x99)
757                                 if ((val & 0xfff) == 0x999)
758                                         return val + 0x00000667;
759                                 else
760                                         return val + 0x00000067;
761                         else if ((val & 0xf) == 0x9)
762                                 return val + 0x00000007;
763                         else
764                                 return val + 1;
765                 }
766
767                 private int CountTrailingZeros ()
768                 {
769                         if (_val1 != 0)
770                                 return CountTrailingZeros (_val1);
771                         if (_val2 != 0)
772                                 return CountTrailingZeros (_val2) + 8;
773                         if (_val3 != 0)
774                                 return CountTrailingZeros (_val3) + 16;
775                         if (_val4 != 0)
776                                 return CountTrailingZeros (_val4) + 24;
777                         return _digitsLen;
778                 }
779
780                 private static int CountTrailingZeros (uint val)
781                 {
782                         if ((val & 0xffff) == 0)
783                                 if ((val & 0xffffff) == 0)
784                                         if ((val & 0xfffffff) == 0)
785                                                 return 7;
786                                         else
787                                                 return 6;
788                                 else if ((val & 0xfffff) == 0)
789                                         return 5;
790                                 else
791                                         return 4;
792                         else if ((val & 0xff) == 0)
793                                 if ((val & 0xfff) == 0)
794                                         return 3;
795                                 else
796                                         return 2;
797                         else if ((val & 0xf) == 0)
798                                 return 1;
799                         else
800                                 return 0;
801                 }
802
803                 #endregion Round
804
805                 #region public number formatting methods
806
807                 public static string NumberToString (string format, sbyte value, NumberFormatInfo nfi)
808                 {
809                         return new NumberFormatter (format, value).NumberToString (format, nfi);
810                 }
811
812                 public static string NumberToString (string format, byte value, NumberFormatInfo nfi)
813                 {
814                         return new NumberFormatter (format, value).NumberToString (format, nfi);
815                 }
816
817                 public static string NumberToString (string format, ushort value, NumberFormatInfo nfi)
818                 {
819                         return new NumberFormatter (format, value).NumberToString (format, nfi);
820                 }
821
822                 public static string NumberToString (string format, short value, NumberFormatInfo nfi)
823                 {
824                         return new NumberFormatter (format, value).NumberToString (format, nfi);
825                 }
826
827                 public static string NumberToString (string format, uint value, NumberFormatInfo nfi)
828                 {
829                         return new NumberFormatter (format, value).NumberToString (format, nfi);
830                 }
831
832                 public static string NumberToString (string format, int value, NumberFormatInfo nfi)
833                 {
834                         return new NumberFormatter (format, value).NumberToString (format, nfi);
835                 }
836
837                 public static string NumberToString (string format, ulong value, NumberFormatInfo nfi)
838                 {
839                         return new NumberFormatter (format, value).NumberToString (format, nfi);
840                 }
841
842                 public static string NumberToString (string format, long value, NumberFormatInfo nfi)
843                 {
844                         return new NumberFormatter (format, value).NumberToString (format, nfi);
845                 }
846
847                 public static string NumberToString (string format, float value, NumberFormatInfo nfi)
848                 {
849                         NumberFormatter rep = new NumberFormatter (format, value);
850                         if (rep._specifier == 'R')
851                                 return rep.FormatRoundtrip (value, nfi);
852                         return rep.NumberToString (format, nfi);
853                 }
854
855                 public static string NumberToString (string format, double value, NumberFormatInfo nfi)
856                 {
857                         NumberFormatter rep = new NumberFormatter (format, value);
858                         if (rep._specifier == 'R')
859                                 return rep.FormatRoundtrip (value, nfi);
860                         return rep.NumberToString (format, nfi);
861                 }
862
863                 public static string NumberToString (string format, decimal value, NumberFormatInfo nfi)
864                 {
865                         return new NumberFormatter (format, value).NumberToString (format, nfi);
866                 }
867
868                 public string NumberToString (string format, NumberFormatInfo nfi)
869                 {
870                         if (IsFloatingSource) {
871                                 if (_NaN)
872                                         return nfi.NaNSymbol;
873
874                                 if (_infinity) {
875                                         if (_positive)
876                                                 return nfi.PositiveInfinitySymbol;
877                                         else
878                                                 return nfi.NegativeInfinitySymbol;
879                                 }
880                         }
881
882                         switch (_specifier) {
883                         case 'C':
884                                 return FormatCurrency (_precision, nfi);
885                         case 'D':
886                                 if (IsIntegerSource)
887                                         return FormatDecimal (_precision, nfi);
888                                 throw new FormatException ();
889                         case 'E':
890                                 return FormatExponential (_precision, nfi);
891                         case 'F':
892                                 return FormatFixedPoint (_precision, nfi);
893                         case 'G':
894                                 if (IsIntegerSource && _precision <= 0)
895                                         return FormatDecimal (-1, nfi);
896                                 return FormatGeneral (_precision, nfi);
897                         case 'N':
898                                 return FormatNumber (_precision, nfi);
899                         case 'P':
900                                 return FormatPercent (_precision, nfi);
901                         case 'X':
902                                 if (IsIntegerSource)
903                                         return FormatHexadecimal (_precision);
904                                 throw new FormatException ("The specified format cannot be used in this instance");
905                         case 'R':
906                                 throw new FormatException ("The specified format cannot be used in this instance");
907                         default:
908                                 if (_isCustomFormat)
909                                         return FormatCustom (format, nfi);
910                                 throw new FormatException ("The specified format '" + format + "' is invalid");
911                         }
912                 }
913
914                 public string FormatCurrency (int precision, NumberFormatInfo nfi)
915                 {
916                         precision = (precision >= 0 ? precision : nfi.CurrencyDecimalDigits);
917                         RoundDecimal (precision);
918                         ResetCharBuf (IntegerDigits * 2 + precision * 2 + 16);
919
920                         if (_positive) {
921                                 switch (nfi.CurrencyPositivePattern) {
922                                 case 0:
923                                         Append (nfi.CurrencySymbol);
924                                         break;
925                                 case 2:
926                                         Append (nfi.CurrencySymbol);
927                                         Append (' ');
928                                         break;
929                                 }
930                         }
931                         else {
932                                 switch (nfi.CurrencyNegativePattern) {
933                                 case 0:
934                                         Append ('(');
935                                         Append (nfi.CurrencySymbol);
936                                         break;
937                                 case 1:
938                                         Append (nfi.NegativeSign);
939                                         Append (nfi.CurrencySymbol);
940                                         break;
941                                 case 2:
942                                         Append (nfi.CurrencySymbol);
943                                         Append (nfi.NegativeSign);
944                                         break;
945                                 case 3:
946                                         Append (nfi.CurrencySymbol);
947                                         break;
948                                 case 4:
949                                         Append ('(');
950                                         break;
951                                 case 5:
952                                         Append (nfi.NegativeSign);
953                                         break;
954                                 case 8:
955                                         Append (nfi.NegativeSign);
956                                         break;
957                                 case 9:
958                                         Append (nfi.NegativeSign);
959                                         Append (nfi.CurrencySymbol);
960                                         Append (' ');
961                                         break;
962                                 case 11:
963                                         Append (nfi.CurrencySymbol);
964                                         Append (' ');
965                                         break;
966                                 case 12:
967                                         Append (nfi.CurrencySymbol);
968                                         Append (' ');
969                                         Append (nfi.NegativeSign);
970                                         break;
971                                 case 14:
972                                         Append ('(');
973                                         Append (nfi.CurrencySymbol);
974                                         Append (' ');
975                                         break;
976                                 case 15:
977                                         Append ('(');
978                                         break;
979                                 }
980                         }
981
982                         AppendIntegerStringWithGroupSeparator (nfi.RawCurrencyGroupSizes, nfi.CurrencyGroupSeparator);
983
984                         if (precision > 0) {
985                                 Append (nfi.CurrencyDecimalSeparator);
986                                 AppendDecimalString (precision);
987                         }
988
989                         if (_positive) {
990                                 switch (nfi.CurrencyPositivePattern) {
991                                 case 1:
992                                         Append (nfi.CurrencySymbol);
993                                         break;
994                                 case 3:
995                                         Append (' ');
996                                         Append (nfi.CurrencySymbol);
997                                         break;
998                                 }
999                         }
1000                         else {
1001                                 switch (nfi.CurrencyNegativePattern) {
1002                                 case 0:
1003                                         Append (')');
1004                                         break;
1005                                 case 3:
1006                                         Append (nfi.NegativeSign);
1007                                         break;
1008                                 case 4:
1009                                         Append (nfi.CurrencySymbol);
1010                                         Append (')');
1011                                         break;
1012                                 case 5:
1013                                         Append (nfi.CurrencySymbol);
1014                                         break;
1015                                 case 6:
1016                                         Append (nfi.NegativeSign);
1017                                         Append (nfi.CurrencySymbol);
1018                                         break;
1019                                 case 7:
1020                                         Append (nfi.CurrencySymbol);
1021                                         Append (nfi.NegativeSign);
1022                                         break;
1023                                 case 8:
1024                                         Append (' ');
1025                                         Append (nfi.CurrencySymbol);
1026                                         break;
1027                                 case 10:
1028                                         Append (' ');
1029                                         Append (nfi.CurrencySymbol);
1030                                         Append (nfi.NegativeSign);
1031                                         break;
1032                                 case 11:
1033                                         Append (nfi.NegativeSign);
1034                                         break;
1035                                 case 13:
1036                                         Append (nfi.NegativeSign);
1037                                         Append (' ');
1038                                         Append (nfi.CurrencySymbol);
1039                                         break;
1040                                 case 14:
1041                                         Append (')');
1042                                         break;
1043                                 case 15:
1044                                         Append (' ');
1045                                         Append (nfi.CurrencySymbol);
1046                                         Append (')');
1047                                         break;
1048                                 }
1049                         }
1050
1051                         return new string (_cbuf, 0, _ind);
1052                 }
1053
1054                 public string FormatDecimal (int precision, NumberFormatInfo nfi)
1055                 {
1056                         if (precision < _digitsLen)
1057                                 precision = _digitsLen;
1058                         if (precision == 0)
1059                                 return "0";
1060
1061                         ResetCharBuf (precision + 1);
1062                         if (!_positive) {
1063                                 if (nfi == null)
1064                                         nfi = NumberFormatInfo.GetInstance (null);
1065
1066                                 Append (nfi.NegativeSign);
1067                         }
1068                         AppendDigits (0, precision);
1069
1070                         return new string (_cbuf, 0, _ind);
1071                 }
1072
1073 #if UNSAFE_TABLES // No unsafe code under TARGET_JVM
1074                 unsafe
1075 #endif
1076                 public string FormatHexadecimal (int precision)
1077                 {
1078                         int size = Math.Max (precision, _decPointPos);
1079 #if UNSAFE_TABLES
1080                         char* digits = _specifierIsUpper ? DigitUpperTable : DigitLowerTable;
1081 #else
1082                         char[] digits = _specifierIsUpper ? DigitUpperTable : DigitLowerTable;
1083 #endif
1084                         ResetCharBuf (size);
1085                         _ind = size;
1086                         ulong val = _val1 | ((ulong)_val2 << 32);
1087                         while (size > 0) {
1088                                 _cbuf [--size] = digits [val & 0xf];
1089                                 val >>= 4;
1090                         }
1091                         return new string (_cbuf, 0, _ind);
1092                 }
1093
1094                 public string FormatFixedPoint (int precision, NumberFormatInfo nfi)
1095                 {
1096                         if (precision == -1)
1097                                 precision = nfi.NumberDecimalDigits;
1098
1099                         RoundDecimal (precision);
1100
1101                         ResetCharBuf (IntegerDigits + precision + 2);
1102
1103                         if (!_positive)
1104                                 Append (nfi.NegativeSign);
1105
1106                         AppendIntegerString (IntegerDigits);
1107
1108                         if (precision > 0) {
1109                                 Append (nfi.NumberDecimalSeparator);
1110                                 AppendDecimalString (precision);
1111                         }
1112
1113                         return new string (_cbuf, 0, _ind);
1114                 }
1115
1116                 public string FormatRoundtrip (double origval, NumberFormatInfo nfi)
1117                 {
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))
1122                                 return shortRep;
1123                         return nfc.FormatGeneral (_defPrecision + 2, nfi);
1124                 }
1125
1126                 public string FormatRoundtrip (float origval, NumberFormatInfo nfi)
1127                 {
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))
1132                                 return shortRep;
1133                         return nfc.FormatGeneral (_defPrecision + 2, nfi);
1134                 }
1135
1136                 public string FormatGeneral (int precision, NumberFormatInfo nfi)
1137                 {
1138                         bool enableExp;
1139                         if (precision == -1) {
1140                                 enableExp = IsFloatingSource;
1141                                 precision = _defPrecision;
1142                         }
1143                         else {
1144                                 enableExp = true;
1145                                 if (precision == 0)
1146                                         precision = _defPrecision;
1147                                 RoundPos (precision);
1148                         }
1149
1150                         int intDigits = _decPointPos;
1151                         int digits = _digitsLen;
1152                         int decDigits = digits - intDigits;
1153
1154                         if ((intDigits > precision || intDigits <= -4) && enableExp)
1155                                 return FormatExponential (digits - 1, nfi, 2);
1156
1157                         if (decDigits < 0)
1158                                 decDigits = 0;
1159                         if (intDigits < 0)
1160                                 intDigits = 0;
1161                         ResetCharBuf (decDigits + intDigits + 3);
1162
1163                         if (!_positive)
1164                                 Append (nfi.NegativeSign);
1165
1166                         if (intDigits == 0)
1167                                 Append ('0');
1168                         else
1169                                 AppendDigits (digits - intDigits, digits);
1170
1171                         if (decDigits > 0) {
1172                                 Append (nfi.NumberDecimalSeparator);
1173                                 AppendDigits (0, decDigits);
1174                         }
1175
1176                         return new string (_cbuf, 0, _ind);
1177                 }
1178
1179                 public string FormatNumber (int precision, NumberFormatInfo nfi)
1180                 {
1181                         precision = (precision >= 0 ? precision : nfi.NumberDecimalDigits);
1182                         ResetCharBuf (IntegerDigits * 3 + precision);
1183                         RoundDecimal (precision);
1184
1185                         if (!_positive) {
1186                                 switch (nfi.NumberNegativePattern) {
1187                                 case 0:
1188                                         Append ('(');
1189                                         break;
1190                                 case 1:
1191                                         Append (nfi.NegativeSign);
1192                                         break;
1193                                 case 2:
1194                                         Append (nfi.NegativeSign);
1195                                         Append (' ');
1196                                         break;
1197                                 }
1198                         }
1199
1200                         AppendIntegerStringWithGroupSeparator (nfi.RawNumberGroupSizes, nfi.NumberGroupSeparator);
1201
1202                         if (precision > 0) {
1203                                 Append (nfi.NumberDecimalSeparator);
1204                                 AppendDecimalString (precision);
1205                         }
1206
1207                         if (!_positive) {
1208                                 switch (nfi.NumberNegativePattern) {
1209                                 case 0:
1210                                         Append (')');
1211                                         break;
1212                                 case 3:
1213                                         Append (nfi.NegativeSign);
1214                                         break;
1215                                 case 4:
1216                                         Append (' ');
1217                                         Append (nfi.NegativeSign);
1218                                         break;
1219                                 }
1220                         }
1221
1222                         return new string (_cbuf, 0, _ind);
1223                 }
1224
1225                 public string FormatPercent (int precision, NumberFormatInfo nfi)
1226                 {
1227                         precision = (precision >= 0 ? precision : nfi.PercentDecimalDigits);
1228                         Multiply10(2);
1229                         RoundDecimal (precision);
1230                         ResetCharBuf (IntegerDigits * 2 + precision + 16);
1231
1232                         if (_positive) {
1233                                 if (nfi.PercentPositivePattern == 2)
1234                                         Append (nfi.PercentSymbol);
1235                         }
1236                         else {
1237                                 switch (nfi.PercentNegativePattern) {
1238                                 case 0:
1239                                         Append (nfi.NegativeSign);
1240                                         break;
1241                                 case 1:
1242                                         Append (nfi.NegativeSign);
1243                                         break;
1244                                 case 2:
1245                                         Append (nfi.NegativeSign);
1246                                         Append (nfi.PercentSymbol);
1247                                         break;
1248                                 }
1249                         }
1250
1251                         AppendIntegerStringWithGroupSeparator (nfi.RawPercentGroupSizes, nfi.PercentGroupSeparator);
1252
1253                         if (precision > 0) {
1254                                 Append (nfi.PercentDecimalSeparator);
1255                                 AppendDecimalString (precision);
1256                         }
1257
1258                         if (_positive) {
1259                                 switch (nfi.PercentPositivePattern) {
1260                                 case 0:
1261                                         Append (' ');
1262                                         Append (nfi.PercentSymbol);
1263                                         break;
1264                                 case 1:
1265                                         Append (nfi.PercentSymbol);
1266                                         break;
1267                                 }
1268                         }
1269                         else {
1270                                 switch (nfi.PercentNegativePattern) {
1271                                 case 0:
1272                                         Append (' ');
1273                                         Append (nfi.PercentSymbol);
1274                                         break;
1275                                 case 1:
1276                                         Append (nfi.PercentSymbol);
1277                                         break;
1278                                 }
1279                         }
1280
1281                         return new string (_cbuf, 0, _ind);
1282                 }
1283
1284                 public string FormatExponential (int precision, NumberFormatInfo nfi)
1285                 {
1286                         if (precision == -1)
1287                                 precision = DefaultExpPrecision;
1288
1289                         RoundPos (precision + 1);
1290                         return FormatExponential (precision, nfi, 3);
1291                 }
1292
1293                 private string FormatExponential (int precision, NumberFormatInfo nfi, int expDigits)
1294                 {
1295                         int decDigits = _decPointPos;
1296                         int digits = _digitsLen;
1297                         int exponent = decDigits - 1;
1298                         decDigits = _decPointPos = 1;
1299
1300                         ResetCharBuf (precision + 8);
1301
1302                         if (!_positive)
1303                                 Append (nfi.NegativeSign);
1304
1305                         AppendOneDigit (digits - 1);
1306
1307                         if (precision > 0) {
1308                                 Append (nfi.NumberDecimalSeparator);
1309                                 AppendDigits (digits - precision - 1, digits - _decPointPos);
1310                         }
1311
1312                         AppendExponent (nfi, exponent, expDigits);
1313
1314                         return new string (_cbuf, 0, _ind);
1315                 }
1316
1317                 public string FormatCustom (string format, NumberFormatInfo nfi)
1318                 {
1319                         bool p = _positive;
1320                         int offset = 0;
1321                         int length = 0;
1322                         CustomInfo.GetActiveSection (format, ref p, IsZero, ref offset, ref length);
1323                         if (length == 0)
1324                                 return _positive ? string.Empty : nfi.NegativeSign;
1325                         _positive = p;
1326
1327                         CustomInfo info = CustomInfo.Parse (format, offset, length, nfi);
1328 #if false
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);
1343 #endif
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);
1347
1348                         int diff = 0;
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);
1355
1356                         bool expPositive = true;
1357                         if (info.UseExponent && (info.DecimalDigits > 0 || info.IntegerDigits > 0)) {
1358                                 if (!IsZero) {
1359                                         RoundPos (info.DecimalDigits + info.IntegerDigits);
1360                                         diff -= _decPointPos - info.IntegerDigits;
1361                                         _decPointPos = info.IntegerDigits;
1362                                 }
1363
1364                                 expPositive = diff <= 0;
1365                                 AppendNonNegativeNumber (sb_exp, diff < 0 ? -diff : diff);
1366                         }
1367                         else
1368                                 RoundDecimal (info.DecimalDigits);
1369
1370                         if (info.IntegerDigits != 0 || !IsZeroInteger)
1371                                 AppendIntegerString (IntegerDigits, sb_int);
1372
1373                         AppendDecimalString (DecimalDigits, sb_dec);
1374
1375                         if (info.UseExponent) {
1376                                 if (info.DecimalDigits <= 0 && info.IntegerDigits <= 0)
1377                                         _positive = true;
1378
1379                                 if (sb_int.Length < info.IntegerDigits)
1380                                         sb_int.Insert (0, "0", info.IntegerDigits - sb_int.Length);
1381
1382                                 while (sb_exp.Length < info.ExponentDigits - info.ExponentTailSharpDigits)
1383                                         sb_exp.Insert (0, '0');
1384
1385                                 if (expPositive && !info.ExponentNegativeSignOnly)
1386                                         sb_exp.Insert (0, nfi.PositiveSign);
1387                                 else if (!expPositive)
1388                                         sb_exp.Insert (0, nfi.NegativeSign);
1389                         }
1390                         else {
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);
1395                         }
1396
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);
1402
1403                         return info.Format (format, offset, length, nfi, _positive, sb_int, sb_dec, sb_exp);
1404                 }
1405                 #endregion public number formatting methods
1406
1407                 #region StringBuilder formatting helpers
1408
1409                 private static void ZeroTrimEnd (StringBuilder sb)
1410                 {
1411                         ZeroTrimEnd (sb, false);
1412                 }
1413
1414                 private static void ZeroTrimEnd (StringBuilder sb, bool canEmpty)
1415                 {
1416                         int len = 0;
1417                         for (int i = sb.Length - 1; (canEmpty ? i >= 0 : i > 0); i--) {
1418                                 if (sb [i] != '0')
1419                                         break;
1420                                 len++;
1421                         }
1422
1423                         if (len > 0)
1424                                 sb.Remove (sb.Length - len, len);
1425                 }
1426
1427                 private static bool IsZeroOnly (StringBuilder sb)
1428                 {
1429                         for (int i = 0; i < sb.Length; i++)
1430                                 if (char.IsDigit (sb [i]) && sb [i] != '0')
1431                                         return false;
1432                         return true;
1433                 }
1434
1435                 private static void AppendNonNegativeNumber (StringBuilder sb, int v)
1436                 {
1437                         if (v < 0)
1438                                 throw new ArgumentException ();
1439
1440                         int i = ScaleOrder (v) - 1;
1441                         do {
1442                                 int n = v / (int)GetTenPowerOf (i);
1443                                 sb.Append ((char)('0' | n));
1444                                 v -= (int)GetTenPowerOf (i--) * n;
1445                         } while (i >= 0);
1446                 }
1447
1448                 #endregion StringBuilder formatting helpers
1449
1450                 #region Append helpers
1451
1452                 private void AppendIntegerString (int minLength, StringBuilder sb)
1453                 {
1454                         if (_decPointPos <= 0) {
1455                                 sb.Append ('0', minLength);
1456                                 return;
1457                         }
1458
1459                         if (_decPointPos < minLength)
1460                                 sb.Append ('0', minLength - _decPointPos);
1461
1462                         AppendDigits (_digitsLen - _decPointPos, _digitsLen, sb);
1463                 }
1464
1465                 private void AppendIntegerString (int minLength)
1466                 {
1467                         if (_decPointPos <= 0) {
1468                                 Append ('0', minLength);
1469                                 return;
1470                         }
1471
1472                         if (_decPointPos < minLength)
1473                                 Append ('0', minLength - _decPointPos);
1474
1475                         AppendDigits (_digitsLen - _decPointPos, _digitsLen);
1476                 }
1477
1478                 private void AppendDecimalString (int precision, StringBuilder sb)
1479                 {
1480                         AppendDigits (_digitsLen - precision - _decPointPos, _digitsLen - _decPointPos, sb);
1481                 }
1482
1483                 private void AppendDecimalString (int precision)
1484                 {
1485                         AppendDigits (_digitsLen - precision - _decPointPos, _digitsLen - _decPointPos);
1486                 }
1487
1488                 private void AppendIntegerStringWithGroupSeparator (int[] groups, string groupSeparator)
1489                 {
1490                         if (IsZeroInteger) {
1491                                 Append ('0');
1492                                 return;
1493                         }
1494
1495                         int total = 0;
1496                         int groupIndex = 0;
1497                         for (int i = 0; i < groups.Length; i++) {
1498                                 total += groups [i];
1499                                 if (total <= _decPointPos)
1500                                         groupIndex = i;
1501                                 else
1502                                         break;
1503                         }
1504
1505                         if (groups.Length > 0 && total > 0) {
1506                                 int counter;
1507                                 int groupSize = groups [groupIndex];
1508                                 int fraction = _decPointPos > total ? _decPointPos - total : 0;
1509                                 if (groupSize == 0) {
1510                                         while (groupIndex >= 0 && groups [groupIndex] == 0)
1511                                                 groupIndex--;
1512
1513                                         groupSize = fraction > 0 ? fraction : groups [groupIndex];
1514                                 }
1515                                 if (fraction == 0)
1516                                         counter = groupSize;
1517                                 else {
1518                                         groupIndex += fraction / groupSize;
1519                                         counter = fraction % groupSize;
1520                                         if (counter == 0)
1521                                                 counter = groupSize;
1522                                         else
1523                                                 groupIndex++;
1524                                 }
1525
1526                                 for (int i = 0; ;) {
1527                                         if ((_decPointPos - i) <= counter || counter == 0) {
1528                                                 AppendDigits (_digitsLen - _decPointPos, _digitsLen - i);
1529                                                 break;
1530                                         }
1531                                         AppendDigits (_digitsLen - i - counter, _digitsLen - i);
1532                                         i += counter;
1533                                         Append (groupSeparator);
1534                                         if (--groupIndex < groups.Length && groupIndex >= 0)
1535                                                 groupSize = groups [groupIndex];
1536                                         counter = groupSize;
1537                                 }
1538                         }
1539                         else {
1540                                 AppendDigits (_digitsLen - _decPointPos, _digitsLen);
1541                         }
1542                 }
1543
1544                 // minDigits is in the range 1..3
1545                 private void AppendExponent (NumberFormatInfo nfi, int exponent, int minDigits)
1546                 {
1547                         if (_specifierIsUpper || _specifier == 'R')
1548                                 Append ('E');
1549                         else
1550                                 Append ('e');
1551
1552                         if (exponent >= 0)
1553                                 Append (nfi.PositiveSign);
1554                         else {
1555                                 Append (nfi.NegativeSign);
1556                                 exponent = -exponent;
1557                         }
1558
1559                         if (exponent == 0)
1560                                 Append ('0', minDigits);
1561                         else if (exponent < 10) {
1562                                 Append ('0', minDigits - 1);
1563                                 Append ((char)('0' | exponent));
1564                         }
1565                         else {
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)));
1571                         }
1572                 }
1573
1574                 private void AppendOneDigit (int start)
1575                 {
1576                         if (_ind == _cbuf.Length)
1577                                 Resize (_ind + 10);
1578
1579                         start += _offset;
1580                         uint v;
1581                         if (start < 0)
1582                                 v = 0;
1583                         else if (start < 8)
1584                                 v = _val1;
1585                         else if (start < 16)
1586                                 v = _val2;
1587                         else if (start < 24)
1588                                 v = _val3;
1589                         else if (start < 32)
1590                                 v = _val4;
1591                         else
1592                                 v = 0;
1593                         v >>= (start & 0x7) << 2;
1594                         _cbuf [_ind++] = (char)('0' | v & 0xf);
1595                 }
1596
1597                 private void AppendDigits (int start, int end)
1598                 {
1599                         if (start >= end)
1600                                 return;
1601
1602                         int i = _ind + (end - start);
1603                         if (i > _cbuf.Length)
1604                                 Resize (i + 10);
1605                         _ind = i;
1606
1607                         end += _offset;
1608                         start += _offset;
1609
1610                         for (int next = start + 8 - (start & 0x7); ; start = next, next += 8) {
1611                                 uint v;
1612                                 if (next == 8)
1613                                         v = _val1;
1614                                 else if (next == 16)
1615                                         v = _val2;
1616                                 else if (next == 24)
1617                                         v = _val3;
1618                                 else if (next == 32)
1619                                         v = _val4;
1620                                 else
1621                                         v = 0;
1622                                 v >>= (start & 0x7) << 2;
1623                                 if (next > end)
1624                                         next = end;
1625
1626                                 _cbuf [--i] = (char)('0' | v & 0xf);
1627                                 switch (next - start) {
1628                                 case 8:
1629                                         _cbuf [--i] = (char)('0' | (v >>= 4) & 0xf);
1630                                         goto case 7;
1631                                 case 7:
1632                                         _cbuf [--i] = (char)('0' | (v >>= 4) & 0xf);
1633                                         goto case 6;
1634                                 case 6:
1635                                         _cbuf [--i] = (char)('0' | (v >>= 4) & 0xf);
1636                                         goto case 5;
1637                                 case 5:
1638                                         _cbuf [--i] = (char)('0' | (v >>= 4) & 0xf);
1639                                         goto case 4;
1640                                 case 4:
1641                                         _cbuf [--i] = (char)('0' | (v >>= 4) & 0xf);
1642                                         goto case 3;
1643                                 case 3:
1644                                         _cbuf [--i] = (char)('0' | (v >>= 4) & 0xf);
1645                                         goto case 2;
1646                                 case 2:
1647                                         _cbuf [--i] = (char)('0' | (v >>= 4) & 0xf);
1648                                         goto case 1;
1649                                 case 1:
1650                                         if (next == end)
1651                                                 return;
1652                                         continue;
1653                                 }
1654                         }
1655                 }
1656
1657                 private void AppendDigits (int start, int end, StringBuilder sb)
1658                 {
1659                         if (start >= end)
1660                                 return;
1661
1662                         int i = sb.Length + (end - start);
1663                         sb.Length = i;
1664
1665                         end += _offset;
1666                         start += _offset;
1667
1668                         for (int next = start + 8 - (start & 0x7); ; start = next, next += 8) {
1669                                 uint v;
1670                                 if (next == 8)
1671                                         v = _val1;
1672                                 else if (next == 16)
1673                                         v = _val2;
1674                                 else if (next == 24)
1675                                         v = _val3;
1676                                 else if (next == 32)
1677                                         v = _val4;
1678                                 else
1679                                         v = 0;
1680                                 v >>= (start & 0x7) << 2;
1681                                 if (next > end)
1682                                         next = end;
1683                                 sb [--i] = (char)('0' | v & 0xf);
1684                                 switch (next - start) {
1685                                 case 8:
1686                                         sb [--i] = (char)('0' | (v >>= 4) & 0xf);
1687                                         goto case 7;
1688                                 case 7:
1689                                         sb [--i] = (char)('0' | (v >>= 4) & 0xf);
1690                                         goto case 6;
1691                                 case 6:
1692                                         sb [--i] = (char)('0' | (v >>= 4) & 0xf);
1693                                         goto case 5;
1694                                 case 5:
1695                                         sb [--i] = (char)('0' | (v >>= 4) & 0xf);
1696                                         goto case 4;
1697                                 case 4:
1698                                         sb [--i] = (char)('0' | (v >>= 4) & 0xf);
1699                                         goto case 3;
1700                                 case 3:
1701                                         sb [--i] = (char)('0' | (v >>= 4) & 0xf);
1702                                         goto case 2;
1703                                 case 2:
1704                                         sb [--i] = (char)('0' | (v >>= 4) & 0xf);
1705                                         goto case 1;
1706                                 case 1:
1707                                         if (next == end)
1708                                                 return;
1709                                         continue;
1710                                 }
1711                         }
1712                 }
1713
1714                 #endregion Append helpers
1715
1716                 #region others
1717
1718                 private void Multiply10(int count)
1719                 {
1720                         if (count <= 0 || _digitsLen == 0)
1721                                 return;
1722
1723                         _decPointPos += count;
1724                 }
1725
1726                 private void Divide10(int count)
1727                 {
1728                         if (count <= 0 || _digitsLen == 0)
1729                                 return;
1730
1731                         _decPointPos -= count;
1732                 }
1733
1734                 private NumberFormatter GetClone ()
1735                 {
1736                         return (NumberFormatter)this.MemberwiseClone ();
1737                 }
1738
1739                 #endregion others
1740
1741                 #region custom
1742
1743                 private class CustomInfo
1744                 {
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;
1759
1760                         public static void GetActiveSection (string format, ref bool positive, bool zero, ref int offset, ref int length)
1761                         {
1762                                 int[] lens = new int [3];
1763                                 int index = 0;
1764                                 int lastPos = 0;
1765                                 char literal = '\0';
1766                                 for (int i = 0; i < format.Length; i++) {
1767                                         char c = format [i];
1768
1769                                         if (c == literal || (literal == '\0' && (c == '\"' || c == '\''))) {
1770                                                 if (literal == '\0')
1771                                                         literal = c;
1772                                                 else
1773                                                         literal = '\0';
1774                                                 continue;
1775                                         }
1776
1777                                         if (literal == '\0' && format [i] == ';' && (i == 0 || format [i - 1] != '\\')) {
1778                                                 lens [index++] = i - lastPos;
1779                                                 lastPos = i + 1;
1780                                                 if (index == 3)
1781                                                         break;
1782                                         }
1783                                 }
1784
1785                                 if (index == 0) {
1786                                         offset = 0;
1787                                         length = format.Length;
1788                                         return;
1789                                 }
1790                                 if (index == 1) {
1791                                         if (positive || zero) {
1792                                                 offset = 0;
1793                                                 length = lens [0];
1794                                                 return;
1795                                         }
1796                                         if (lens [0] + 1 < format.Length) {
1797                                                 positive = true;
1798                                                 offset = lens [0] + 1;
1799                                                 length = format.Length - offset;
1800                                                 return;
1801                                         }
1802                                         else {
1803                                                 offset = 0;
1804                                                 length = lens [0];
1805                                                 return;
1806                                         }
1807                                 }
1808                                 if (index == 2) {
1809                                         if (zero) {
1810                                                 offset = lens [0] + lens [1] + 2;
1811                                                 length = format.Length - offset;
1812                                                 return;
1813                                         }
1814                                         if (positive) {
1815                                                 offset = 0;
1816                                                 length = lens [0];
1817                                                 return;
1818                                         }
1819                                         if (lens [1] > 0) {
1820                                                 positive = true;
1821                                                 offset = lens [0] + 1;
1822                                                 length = lens [1];
1823                                                 return;
1824                                         }
1825                                         else {
1826                                                 offset = 0;
1827                                                 length = lens [0];
1828                                                 return;
1829                                         }
1830                                 }
1831                                 if (index == 3) {
1832                                         if (zero) {
1833                                                 offset = lens [0] + lens [1] + 2;
1834                                                 length = lens [2];
1835                                                 return;
1836                                         }
1837                                         if (positive) {
1838                                                 offset = 0;
1839                                                 length = lens [0];
1840                                                 return;
1841                                         }
1842                                         if (lens [1] > 0) {
1843                                                 positive = true;
1844                                                 offset = lens [0] + 1;
1845                                                 length = lens [1];
1846                                                 return;
1847                                         }
1848                                         else {
1849                                                 offset = 0;
1850                                                 length = lens [0];
1851                                                 return;
1852                                         }
1853                                 }
1854
1855                                 throw new ArgumentException ();
1856                         }
1857
1858                         public static CustomInfo Parse (string format, int offset, int length, NumberFormatInfo nfi)
1859                         {
1860                                 char literal = '\0';
1861                                 bool integerArea = true;
1862                                 bool decimalArea = false;
1863                                 bool exponentArea = false;
1864                                 bool sharpContinues = true;
1865
1866                                 CustomInfo info = new CustomInfo ();
1867                                 int groupSeparatorCounter = 0;
1868
1869                                 for (int i = offset; i - offset < length; i++) {
1870                                         char c = format [i];
1871
1872                                         if (c == literal && c != '\0') {
1873                                                 literal = '\0';
1874                                                 continue;
1875                                         }
1876                                         if (literal != '\0')
1877                                                 continue;
1878
1879                                         if (exponentArea && (c != '\0' && c != '0' && c != '#')) {
1880                                                 exponentArea = false;
1881                                                 integerArea = (info.DecimalPointPos < 0);
1882                                                 decimalArea = !integerArea;
1883                                                 i--;
1884                                                 continue;
1885                                         }
1886
1887                                         switch (c) {
1888                                         case '\\':
1889                                                 i++;
1890                                                 continue;
1891                                         case '\'':
1892                                         case '\"':
1893                                                 if (c == '\"' || c == '\'') {
1894                                                         literal = c;
1895                                                 }
1896                                                 continue;
1897                                         case '#':
1898                                                 if (sharpContinues && integerArea)
1899                                                         info.IntegerHeadSharpDigits++;
1900                                                 else if (decimalArea)
1901                                                         info.DecimalTailSharpDigits++;
1902                                                 else if (exponentArea)
1903                                                         info.ExponentTailSharpDigits++;
1904
1905                                                 goto case '0';
1906                                         case '0':
1907                                                 if (c != '#') {
1908                                                         sharpContinues = false;
1909                                                         if (decimalArea)
1910                                                                 info.DecimalTailSharpDigits = 0;
1911                                                         else if (exponentArea)
1912                                                                 info.ExponentTailSharpDigits = 0;
1913                                                 }
1914                                                 if (info.IntegerHeadPos == -1)
1915                                                         info.IntegerHeadPos = i;
1916
1917                                                 if (integerArea) {
1918                                                         info.IntegerDigits++;
1919                                                         if (groupSeparatorCounter > 0)
1920                                                                 info.UseGroup = true;
1921                                                         groupSeparatorCounter = 0;
1922                                                 }
1923                                                 else if (decimalArea)
1924                                                         info.DecimalDigits++;
1925                                                 else if (exponentArea)
1926                                                         info.ExponentDigits++;
1927                                                 break;
1928                                         case 'e':
1929                                         case 'E':
1930                                                 if (info.UseExponent)
1931                                                         break;
1932
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];
1939                                                         if (nc == '+')
1940                                                                 info.ExponentNegativeSignOnly = false;
1941                                                         if (nc == '+' || nc == '-')
1942                                                                 i++;
1943                                                         else if (nc != '0' && nc != '#') {
1944                                                                 info.UseExponent = false;
1945                                                                 if (info.DecimalPointPos < 0)
1946                                                                         integerArea = true;
1947                                                         }
1948                                                 }
1949
1950                                                 break;
1951                                         case '.':
1952                                                 integerArea = false;
1953                                                 decimalArea = true;
1954                                                 exponentArea = false;
1955                                                 if (info.DecimalPointPos == -1)
1956                                                         info.DecimalPointPos = i;
1957                                                 break;
1958                                         case '%':
1959                                                 info.Percents++;
1960                                                 break;
1961                                         case '\u2030':
1962                                                 info.Permilles++;
1963                                                 break;
1964                                         case ',':
1965                                                 if (integerArea && info.IntegerDigits > 0)
1966                                                         groupSeparatorCounter++;
1967                                                 break;
1968                                         default:
1969                                                 break;
1970                                         }
1971                                 }
1972
1973                                 if (info.ExponentDigits == 0)
1974                                         info.UseExponent = false;
1975                                 else
1976                                         info.IntegerHeadSharpDigits = 0;
1977
1978                                 if (info.DecimalDigits == 0)
1979                                         info.DecimalPointPos = -1;
1980
1981                                 info.DividePlaces += groupSeparatorCounter * 3;
1982
1983                                 return info;
1984                         }
1985
1986                         public string Format (string format, int offset, int length, NumberFormatInfo nfi, bool positive, StringBuilder sb_int, StringBuilder sb_dec, StringBuilder sb_exp)
1987                         {
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;
1995
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)
2004                                                         groupIndex = i;
2005                                         }
2006                                         groupSize = groups [groupIndex];
2007                                         int fraction = intLen > total ? intLen - total : 0;
2008                                         if (groupSize == 0) {
2009                                                 while (groupIndex >= 0 && groups [groupIndex] == 0)
2010                                                         groupIndex--;
2011
2012                                                 groupSize = fraction > 0 ? fraction : groups [groupIndex];
2013                                         }
2014                                         if (fraction == 0)
2015                                                 counter = groupSize;
2016                                         else {
2017                                                 groupIndex += fraction / groupSize;
2018                                                 counter = fraction % groupSize;
2019                                                 if (counter == 0)
2020                                                         counter = groupSize;
2021                                                 else
2022                                                         groupIndex++;
2023                                         }
2024                                 }
2025                                 else
2026                                         UseGroup = false;
2027
2028                                 for (int i = offset; i - offset < length; i++) {
2029                                         char c = format [i];
2030
2031                                         if (c == literal && c != '\0') {
2032                                                 literal = '\0';
2033                                                 continue;
2034                                         }
2035                                         if (literal != '\0') {
2036                                                 sb.Append (c);
2037                                                 continue;
2038                                         }
2039
2040                                         switch (c) {
2041                                         case '\\':
2042                                                 i++;
2043                                                 if (i - offset < length)
2044                                                         sb.Append (format [i]);
2045                                                 continue;
2046                                         case '\'':
2047                                         case '\"':
2048                                                 if (c == '\"' || c == '\'')
2049                                                         literal = c;
2050                                                 continue;
2051                                         case '#':
2052                                                 goto case '0';
2053                                         case '0':
2054                                                 if (integerArea) {
2055                                                         intSharpCounter++;
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;
2064                                                                         }
2065                                                                 }
2066                                                         break;
2067                                                 }
2068                                                 else if (decimalArea) {
2069                                                         if (sb_dec_index < sb_dec.Length)
2070                                                                 sb.Append (sb_dec [sb_dec_index++]);
2071                                                         break;
2072                                                 }
2073
2074                                                 sb.Append (c);
2075                                                 break;
2076                                         case 'e':
2077                                         case 'E':
2078                                                 if (sb_exp == null || !UseExponent) {
2079                                                         sb.Append (c);
2080                                                         break;
2081                                                 }
2082
2083                                                 bool flag1 = true;
2084                                                 bool flag2 = false;
2085
2086                                                 int q;
2087                                                 for (q = i + 1; q - offset < length; q++) {
2088                                                         if (format [q] == '0') {
2089                                                                 flag2 = true;
2090                                                                 continue;
2091                                                         }
2092                                                         if (q == i + 1 && (format [q] == '+' || format [q] == '-'))
2093                                                                 continue;
2094                                                         if (!flag2)
2095                                                                 flag1 = false;
2096                                                         break;
2097                                                 }
2098
2099                                                 if (flag1) {
2100                                                         i = q - 1;
2101                                                         integerArea = (DecimalPointPos < 0);
2102                                                         decimalArea = !integerArea;
2103
2104                                                         sb.Append (c);
2105                                                         sb.Append (sb_exp);
2106                                                         sb_exp = null;
2107                                                 }
2108                                                 else
2109                                                         sb.Append (c);
2110
2111                                                 break;
2112                                         case '.':
2113                                                 if (DecimalPointPos == i) {
2114                                                         if (DecimalDigits > 0) {
2115                                                                 while (sb_int_index < sb_int.Length)
2116                                                                         sb.Append (sb_int [sb_int_index++]);
2117                                                         }
2118                                                         if (sb_dec.Length > 0)
2119                                                                 sb.Append (nfi.NumberDecimalSeparator);
2120                                                 }
2121                                                 integerArea = false;
2122                                                 decimalArea = true;
2123                                                 break;
2124                                         case ',':
2125                                                 break;
2126                                         case '%':
2127                                                 sb.Append (nfi.PercentSymbol);
2128                                                 break;
2129                                         case '\u2030':
2130                                                 sb.Append (nfi.PerMilleSymbol);
2131                                                 break;
2132                                         default:
2133                                                 sb.Append (c);
2134                                                 break;
2135                                         }
2136                                 }
2137
2138                                 if (!positive)
2139                                         sb.Insert (0, nfi.NegativeSign);
2140
2141                                 return sb.ToString ();
2142                         }
2143                 }
2144
2145                 #endregion
2146         }
2147 }