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