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