move FrameworkName from corlib to System
[mcs.git] / class / corlib / System / NumberFormatter.cs
blob673d05fea69423f93357c8f7eeb9e04edb865f97
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)
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.
31 // NumberFormatter is shared with Grasshopper and hence the #if TARGET_JVM for
32 // marking the use of unsafe code that is not supported in Grasshopper.
33 #if !TARGET_JVM
34 #define UNSAFE_TABLES
35 #endif
37 using System.Globalization;
38 using System.Text;
39 using System.Threading;
40 using System.Runtime.CompilerServices;
42 namespace System
44 internal sealed partial class NumberFormatter
46 #region Static Fields
48 const int DefaultExpPrecision = 6;
49 const int HundredMillion = 100000000;
50 const long SeventeenDigitsThreshold = 10000000000000000;
51 const ulong ULongDivHundredMillion = UInt64.MaxValue / HundredMillion;
52 const ulong ULongModHundredMillion = 1 + UInt64.MaxValue % HundredMillion;
54 const int DoubleBitsExponentShift = 52;
55 const int DoubleBitsExponentMask = 0x7ff;
56 const long DoubleBitsMantissaMask = 0xfffffffffffff;
57 const int DecimalBitsScaleMask = 0x1f0000;
59 const int SingleDefPrecision = 7;
60 const int DoubleDefPrecision = 15;
61 const int Int8DefPrecision = 3;
62 const int UInt8DefPrecision = 3;
63 const int Int16DefPrecision = 5;
64 const int UInt16DefPrecision = 5;
65 const int Int32DefPrecision = 10;
66 const int UInt32DefPrecision = 10;
67 const int Int64DefPrecision = 19;
68 const int UInt64DefPrecision = 20;
69 const int DecimalDefPrecision = 100;
70 const int TenPowersListLength = 19;
72 const double MinRoundtripVal = -1.79769313486231E+308;
73 const double MaxRoundtripVal = 1.79769313486231E+308;
75 #if UNSAFE_TABLES
76 // The below arrays are taken from mono/metatdata/number-formatter.h
78 private static readonly unsafe ulong* MantissaBitsTable;
79 private static readonly unsafe int* TensExponentTable;
80 private static readonly unsafe char* DigitLowerTable;
81 private static readonly unsafe char* DigitUpperTable;
82 private static readonly unsafe long* TenPowersList;
84 // DecHexDigits s a translation table from a decimal number to its
85 // digits hexadecimal representation (e.g. DecHexDigits [34] = 0x34).
86 private static readonly unsafe int* DecHexDigits;
88 [MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.InternalCall)]
89 private unsafe static extern void GetFormatterTables (out ulong* MantissaBitsTable, out int* TensExponentTable,
90 out char* DigitLowerTable, out char* DigitUpperTable,
91 out long* TenPowersList, out int* DecHexDigits);
93 unsafe static NumberFormatter()
95 GetFormatterTables (out MantissaBitsTable, out TensExponentTable,
96 out DigitLowerTable, out DigitUpperTable, out TenPowersList, out DecHexDigits);
99 unsafe
100 #endif
101 static long GetTenPowerOf(int i)
103 return TenPowersList [i];
105 #endregion Static Fields
107 #region Fields
109 private NumberFormatInfo _nfi;
111 private bool _NaN;
112 private bool _infinity;
113 private bool _isCustomFormat;
114 private bool _specifierIsUpper;
115 private bool _positive;
116 private char _specifier;
117 private int _precision;
118 private int _defPrecision;
120 private int _digitsLen;
121 private int _offset; // Represent the first digit offset.
122 private int _decPointPos;
124 // The following fields are a hexadeimal representation of the digits.
125 // For instance _val = 0x234 represents the digits '2', '3', '4'.
126 private uint _val1; // Digits 0 - 7.
127 private uint _val2; // Digits 8 - 15.
128 private uint _val3; // Digits 16 - 23.
129 private uint _val4; // Digits 23 - 31. Only needed for decimals.
131 #endregion Fields
133 #region Constructor Helpers
135 // Translate an unsigned int to hexadecimal digits.
136 // i.e. 123456789 is represented by _val1 = 0x23456789 and _val2 = 0x1
137 private void InitDecHexDigits (uint value)
139 if (value >= HundredMillion) {
140 int div1 = (int)(value / HundredMillion);
141 value -= HundredMillion * (uint)div1;
142 _val2 = FastToDecHex (div1);
144 _val1 = ToDecHex ((int)value);
147 // Translate an unsigned long to hexadecimal digits.
148 private void InitDecHexDigits (ulong value)
150 if (value >= HundredMillion) {
151 long div1 = (long)(value / HundredMillion);
152 value -= HundredMillion * (ulong)div1;
153 if (div1 >= HundredMillion) {
154 int div2 = (int)(div1 / HundredMillion);
155 div1 = div1 - div2 * (long)HundredMillion;
156 _val3 = ToDecHex (div2);
158 if (div1 != 0)
159 _val2 = ToDecHex ((int)(div1));
161 if (value != 0)
162 _val1 = ToDecHex ((int)value);
165 // Translate a decimal integer to hexadecimal digits.
166 // The decimal integer is 96 digits and its value is hi * 2^64 + lo.
167 // is the lower 64 bits.
168 private void InitDecHexDigits (uint hi, ulong lo)
170 if (hi == 0) {
171 InitDecHexDigits (lo); // Only the lower 64 bits matter.
172 return;
175 // Compute (hi, lo) = (hi , lo) / HundredMillion.
176 uint divhi = hi / HundredMillion;
177 ulong remhi = hi - divhi * HundredMillion;
178 ulong divlo = lo / HundredMillion;
179 ulong remlo = lo - divlo * HundredMillion + remhi * ULongModHundredMillion;
180 hi = divhi;
181 lo = divlo + remhi * ULongDivHundredMillion;
182 divlo = remlo / HundredMillion;
183 remlo -= divlo * HundredMillion;
184 lo += divlo;
185 _val1 = ToDecHex ((int)remlo);
187 // Divide hi * 2 ^ 64 + lo by HundredMillion using the fact that
188 // hi < HundredMillion.
189 divlo = lo / HundredMillion;
190 remlo = lo - divlo * HundredMillion;
191 lo = divlo;
192 if (hi != 0) {
193 lo += hi * ULongDivHundredMillion;
194 remlo += hi * ULongModHundredMillion;
195 divlo = remlo / HundredMillion;
196 lo += divlo;
197 remlo -= divlo * HundredMillion;
199 _val2 = ToDecHex ((int)remlo);
201 // Now we are left with 64 bits store in lo.
202 if (lo >= HundredMillion) {
203 divlo = lo / HundredMillion;
204 lo -= divlo * HundredMillion;
205 _val4 = ToDecHex ((int)divlo);
207 _val3 = ToDecHex ((int)lo);
210 // Helper to translate an int in the range 0 .. 9999 to its
211 // Hexadecimal digits representation.
212 #if UNSAFE_TABLES
213 unsafe
214 #endif
215 private static uint FastToDecHex (int val)
217 if (val < 100)
218 return (uint)DecHexDigits [val];
220 // Uses 2^19 (524288) to compute val / 100 for val < 10000.
221 int v = (val * 5243) >> 19;
222 return (uint)((DecHexDigits [v] << 8) | DecHexDigits [val - v * 100]);
225 // Helper to translate an int in the range 0 .. 99999999 to its
226 // Hexadecimal digits representation.
227 private static uint ToDecHex (int val)
229 uint res = 0;
230 if (val >= 10000) {
231 int v = val / 10000;
232 val -= v * 10000;
233 res = FastToDecHex (v) << 16;
235 return res | FastToDecHex (val);
238 // Helper to count number of hexadecimal digits in a number.
239 private static int FastDecHexLen (int val)
241 if (val < 0x100)
242 if (val < 0x10)
243 return 1;
244 else
245 return 2;
246 else if (val < 0x1000)
247 return 3;
248 else
249 return 4;
252 private static int DecHexLen (uint val)
254 if (val < 0x10000)
255 return FastDecHexLen ((int)val);
256 return 4 + FastDecHexLen ((int)(val >> 16));
259 // Count number of hexadecimal digits stored in _val1 .. _val4.
260 private int DecHexLen ()
262 if (_val4 != 0)
263 return DecHexLen (_val4) + 24;
264 else if (_val3 != 0)
265 return DecHexLen (_val3) + 16;
266 else if (_val2 != 0)
267 return DecHexLen (_val2) + 8;
268 else if (_val1 != 0)
269 return DecHexLen (_val1);
270 else
271 return 0;
274 // Helper to count the 10th scale (number of digits) in a number
275 private static int ScaleOrder (long hi)
277 for (int i = TenPowersListLength - 1; i >= 0; i--)
278 if (hi >= GetTenPowerOf (i))
279 return i + 1;
280 return 1;
283 // Compute the initial precision for rounding a floating number
284 // according to the used format.
285 int InitialFloatingPrecision ()
287 if (_specifier == 'R')
288 return _defPrecision + 2;
289 if (_precision < _defPrecision)
290 return _defPrecision;
291 if (_specifier == 'G')
292 return Math.Min (_defPrecision + 2, _precision);
293 if (_specifier == 'E')
294 return Math.Min (_defPrecision + 2, _precision + 1);
295 return _defPrecision;
298 // Parse the given format and extract the precision in it.
299 // Returns -1 for empty formats and -2 to indicate that the format
300 // is a custom format.
301 private static int ParsePrecision (string format)
303 int precision = 0;
304 for (int i = 1; i < format.Length; i++) {
305 int val = format [i] - '0';
306 precision = precision * 10 + val;
307 if (val < 0 || val > 9 || precision > 99)
308 return -2;
310 return precision;
313 #endregion Constructor Helpers
315 #region Constructors
317 // Parse the given format and initialize the following fields:
318 // _isCustomFormat, _specifierIsUpper, _specifier & _precision.
319 public NumberFormatter (Thread current)
321 _cbuf = new char [0];
322 if (current == null)
323 return;
324 CurrentCulture = current.CurrentCulture;
327 private void Init (string format)
329 _val1 = _val2 = _val3 = _val4 = 0;
330 _offset = 0;
331 _NaN = _infinity = false;
332 _isCustomFormat = false;
333 _specifierIsUpper = true;
334 _precision = -1;
336 if (format == null || format.Length == 0) {
337 _specifier = 'G';
338 return;
341 char specifier = format [0];
342 if (specifier >= 'a' && specifier <= 'z') {
343 specifier = (char)(specifier - 'a' + 'A');
344 _specifierIsUpper = false;
346 else if (specifier < 'A' || specifier > 'Z') {
347 _isCustomFormat = true;
348 _specifier = '0';
349 return;
351 _specifier = specifier;
352 if (format.Length > 1) {
353 _precision = ParsePrecision (format);
354 if (_precision == -2) { // Is it a custom format?
355 _isCustomFormat = true;
356 _specifier = '0';
357 _precision = -1;
362 private void InitHex (ulong value)
364 switch (_defPrecision) {
365 case Int8DefPrecision: value = (byte) value; break;
366 case Int16DefPrecision: value = (ushort) value; break;
367 case Int32DefPrecision: value = (uint) value; break;
369 _val1 = (uint)value;
370 _val2 = (uint)(value >> 32);
371 _decPointPos = _digitsLen = DecHexLen ();
372 if (value == 0)
373 _decPointPos = 1;
376 private void Init (string format, int value, int defPrecision)
378 Init (format);
379 _defPrecision = defPrecision;
380 _positive = value >= 0;
382 if (value == 0 || _specifier == 'X') {
383 InitHex ((ulong)value);
384 return;
387 if (value < 0)
388 value = -value;
389 InitDecHexDigits ((uint)value);
390 _decPointPos = _digitsLen = DecHexLen ();
393 private void Init (string format, uint value, int defPrecision)
395 Init (format);
396 _defPrecision = defPrecision;
397 _positive = true;
399 if (value == 0 || _specifier == 'X') {
400 InitHex (value);
401 return;
404 InitDecHexDigits (value);
405 _decPointPos = _digitsLen = DecHexLen ();
408 private void Init (string format, long value)
410 Init (format);
411 _defPrecision = Int64DefPrecision;
412 _positive = value >= 0;
414 if (value == 0 || _specifier == 'X') {
415 InitHex ((ulong)value);
416 return;
419 if (value < 0)
420 value = -value;
421 InitDecHexDigits ((ulong)value);
422 _decPointPos = _digitsLen = DecHexLen ();
425 private void Init (string format, ulong value)
427 Init (format);
428 _defPrecision = UInt64DefPrecision;
429 _positive = true;
431 if (value == 0 || _specifier == 'X') {
432 InitHex ((ulong)value);
433 return;
436 InitDecHexDigits (value);
437 _decPointPos = _digitsLen = DecHexLen ();
440 #if UNSAFE_TABLES // No unsafe code under TARGET_JVM
441 unsafe
442 #endif
443 private void Init (string format, double value, int defPrecision)
445 Init (format);
447 _defPrecision = defPrecision;
448 long bits = BitConverter.DoubleToInt64Bits (value);
449 _positive = bits >= 0;
450 bits &= Int64.MaxValue;
451 if (bits == 0) {
452 _decPointPos = 1;
453 _digitsLen = 0;
454 _positive = true;
455 return;
458 int e = (int)(bits >> DoubleBitsExponentShift);
459 long m = bits & DoubleBitsMantissaMask;
460 if (e == DoubleBitsExponentMask) {
461 _NaN = m != 0;
462 _infinity = m == 0;
463 return;
466 int expAdjust = 0;
467 if (e == 0) {
468 // We need 'm' to be large enough so we won't lose precision.
469 e = 1;
470 int scale = ScaleOrder (m);
471 if (scale < DoubleDefPrecision) {
472 expAdjust = scale - DoubleDefPrecision;
473 m *= GetTenPowerOf (-expAdjust);
476 else {
477 m = (m + DoubleBitsMantissaMask + 1) * 10;
478 expAdjust = -1;
481 // multiply the mantissa by 10 ^ N
482 ulong lo = (uint)m;
483 ulong hi = (ulong)m >> 32;
484 ulong lo2 = MantissaBitsTable [e];
485 ulong hi2 = lo2 >> 32;
486 lo2 = (uint)lo2;
487 ulong mm = hi * lo2 + lo * hi2 + ((lo * lo2) >> 32);
488 long res = (long)(hi * hi2 + (mm >> 32));
489 while (res < SeventeenDigitsThreshold) {
490 mm = (mm & UInt32.MaxValue) * 10;
491 res = res * 10 + (long)(mm >> 32);
492 expAdjust--;
494 if ((mm & 0x80000000) != 0)
495 res++;
497 int order = DoubleDefPrecision + 2;
498 _decPointPos = TensExponentTable [e] + expAdjust + order;
500 // Rescale 'res' to the initial precision (15-17 for doubles).
501 int initialPrecision = InitialFloatingPrecision ();
502 if (order > initialPrecision) {
503 long val = GetTenPowerOf (order - initialPrecision);
504 res = (res + (val >> 1)) / val;
505 order = initialPrecision;
507 if (res >= GetTenPowerOf (order)) {
508 order++;
509 _decPointPos++;
512 InitDecHexDigits ((ulong)res);
513 _offset = CountTrailingZeros ();
514 _digitsLen = order - _offset;
517 private void Init (string format, decimal value)
519 Init (format);
520 _defPrecision = DecimalDefPrecision;
522 int[] bits = decimal.GetBits (value);
523 int scale = (bits [3] & DecimalBitsScaleMask) >> 16;
524 _positive = bits [3] >= 0;
525 if (bits [0] == 0 && bits [1] == 0 && bits [2] == 0) {
526 _decPointPos = -scale;
527 _positive = true;
528 _digitsLen = 0;
529 return;
532 InitDecHexDigits ((uint)bits [2], ((ulong)bits [1] << 32) | (uint)bits [0]);
533 _digitsLen = DecHexLen ();
534 _decPointPos = _digitsLen - scale;
535 if (_precision != -1 || _specifier != 'G') {
536 _offset = CountTrailingZeros ();
537 _digitsLen -= _offset;
541 #endregion Constructors
543 #region Inner String Buffer
545 private char[] _cbuf;
546 private int _ind;
548 private void ResetCharBuf (int size)
550 _ind = 0;
551 if (_cbuf.Length < size)
552 _cbuf = new char [size];
555 private void Resize (int len)
557 char[] newBuf = new char [len];
558 Array.Copy (_cbuf, newBuf, _ind);
559 _cbuf = newBuf;
562 private void Append (char c)
564 if (_ind == _cbuf.Length)
565 Resize (_ind + 10);
566 _cbuf [_ind++] = c;
569 private void Append (char c, int cnt)
571 if (_ind + cnt > _cbuf.Length)
572 Resize (_ind + cnt + 10);
573 while (cnt-- > 0)
574 _cbuf [_ind++] = c;
577 private void Append (string s)
579 int slen = s.Length;
580 if (_ind + slen > _cbuf.Length)
581 Resize (_ind + slen + 10);
582 for (int i = 0; i < slen; i++)
583 _cbuf [_ind++] = s [i];
586 #endregion Inner String Buffer
588 #region Helper properties
590 private NumberFormatInfo GetNumberFormatInstance (IFormatProvider fp)
592 if (_nfi != null && fp == null)
593 return _nfi;
594 return NumberFormatInfo.GetInstance (fp);
597 public CultureInfo CurrentCulture {
598 set {
599 if (value != null && value.IsReadOnly)
600 _nfi = value.NumberFormat;
601 else
602 _nfi = null;
606 private int IntegerDigits {
607 get { return _decPointPos > 0 ? _decPointPos : 1; }
610 private int DecimalDigits {
611 get { return _digitsLen > _decPointPos ? _digitsLen - _decPointPos : 0; }
614 private bool IsFloatingSource {
615 get { return _defPrecision == DoubleDefPrecision || _defPrecision == SingleDefPrecision; }
618 private bool IsZero {
619 get { return _digitsLen == 0; }
622 private bool IsZeroInteger {
623 get { return _digitsLen == 0 || _decPointPos <= 0; }
626 #endregion Helper properties
628 #region Round
630 private void RoundPos (int pos)
632 RoundBits (_digitsLen - pos);
635 private bool RoundDecimal (int decimals)
637 return RoundBits (_digitsLen - _decPointPos - decimals);
640 private bool RoundBits (int shift)
642 if (shift <= 0)
643 return false;
645 if (shift > _digitsLen) {
646 _digitsLen = 0;
647 _decPointPos = 1;
648 _val1 = _val2 = _val3 = _val4 = 0;
649 _positive = true;
650 return false;
652 shift += _offset;
653 _digitsLen += _offset;
654 while (shift > 8) {
655 _val1 = _val2;
656 _val2 = _val3;
657 _val3 = _val4;
658 _val4 = 0;
659 _digitsLen -= 8;
660 shift -= 8;
662 shift = (shift - 1) << 2;
663 uint v = _val1 >> shift;
664 uint rem16 = v & 0xf;
665 _val1 = (v ^ rem16) << shift;
666 bool res = false;
667 if (rem16 >= 0x5) {
668 _val1 |= 0x99999999 >> (28 - shift);
669 AddOneToDecHex ();
670 int newlen = DecHexLen ();
671 res = newlen != _digitsLen;
672 _decPointPos = _decPointPos + newlen - _digitsLen;
673 _digitsLen = newlen;
675 RemoveTrailingZeros ();
676 return res;
679 private void RemoveTrailingZeros ()
681 _offset = CountTrailingZeros ();
682 _digitsLen -= _offset;
683 if (_digitsLen == 0) {
684 _offset = 0;
685 _decPointPos = 1;
686 _positive = true;
690 private void AddOneToDecHex ()
692 if (_val1 == 0x99999999) {
693 _val1 = 0;
694 if (_val2 == 0x99999999) {
695 _val2 = 0;
696 if (_val3 == 0x99999999) {
697 _val3 = 0;
698 _val4 = AddOneToDecHex (_val4);
700 else
701 _val3 = AddOneToDecHex (_val3);
703 else
704 _val2 = AddOneToDecHex (_val2);
706 else
707 _val1 = AddOneToDecHex (_val1);
710 // Assume val != 0x99999999
711 private static uint AddOneToDecHex (uint val)
713 if ((val & 0xffff) == 0x9999)
714 if ((val & 0xffffff) == 0x999999)
715 if ((val & 0xfffffff) == 0x9999999)
716 return val + 0x06666667;
717 else
718 return val + 0x00666667;
719 else if ((val & 0xfffff) == 0x99999)
720 return val + 0x00066667;
721 else
722 return val + 0x00006667;
723 else if ((val & 0xff) == 0x99)
724 if ((val & 0xfff) == 0x999)
725 return val + 0x00000667;
726 else
727 return val + 0x00000067;
728 else if ((val & 0xf) == 0x9)
729 return val + 0x00000007;
730 else
731 return val + 1;
734 private int CountTrailingZeros ()
736 if (_val1 != 0)
737 return CountTrailingZeros (_val1);
738 if (_val2 != 0)
739 return CountTrailingZeros (_val2) + 8;
740 if (_val3 != 0)
741 return CountTrailingZeros (_val3) + 16;
742 if (_val4 != 0)
743 return CountTrailingZeros (_val4) + 24;
744 return _digitsLen;
747 private static int CountTrailingZeros (uint val)
749 if ((val & 0xffff) == 0)
750 if ((val & 0xffffff) == 0)
751 if ((val & 0xfffffff) == 0)
752 return 7;
753 else
754 return 6;
755 else if ((val & 0xfffff) == 0)
756 return 5;
757 else
758 return 4;
759 else if ((val & 0xff) == 0)
760 if ((val & 0xfff) == 0)
761 return 3;
762 else
763 return 2;
764 else if ((val & 0xf) == 0)
765 return 1;
766 else
767 return 0;
770 #endregion Round
772 #region public number formatting methods
774 [ThreadStatic]
775 static NumberFormatter threadNumberFormatter;
777 private static NumberFormatter GetInstance()
779 NumberFormatter res = threadNumberFormatter;
780 threadNumberFormatter = null;
781 if (res == null)
782 return new NumberFormatter (Thread.CurrentThread);
783 return res;
786 private void Release()
788 threadNumberFormatter = this;
791 internal static void SetThreadCurrentCulture (CultureInfo culture)
793 if (threadNumberFormatter != null)
794 threadNumberFormatter.CurrentCulture = culture;
797 public static string NumberToString (string format, sbyte value, IFormatProvider fp)
799 NumberFormatter inst = GetInstance();
800 inst.Init (format, value, Int8DefPrecision);
801 string res = inst.IntegerToString (format, fp);
802 inst.Release();
803 return res;
806 public static string NumberToString (string format, byte value, IFormatProvider fp)
808 NumberFormatter inst = GetInstance();
809 inst.Init (format, value, UInt8DefPrecision);
810 string res = inst.IntegerToString (format, fp);
811 inst.Release();
812 return res;
815 public static string NumberToString (string format, ushort value, IFormatProvider fp)
817 NumberFormatter inst = GetInstance();
818 inst.Init (format, value, Int16DefPrecision);
819 string res = inst.IntegerToString (format, fp);
820 inst.Release();
821 return res;
824 public static string NumberToString (string format, short value, IFormatProvider fp)
826 NumberFormatter inst = GetInstance();
827 inst.Init (format, value, UInt16DefPrecision);
828 string res = inst.IntegerToString (format, fp);
829 inst.Release();
830 return res;
833 public static string NumberToString (string format, uint value, IFormatProvider fp)
835 NumberFormatter inst = GetInstance();
836 inst.Init (format, value, Int32DefPrecision);
837 string res = inst.IntegerToString (format, fp);
838 inst.Release();
839 return res;
842 public static string NumberToString (string format, int value, IFormatProvider fp)
844 NumberFormatter inst = GetInstance();
845 inst.Init (format, value, UInt32DefPrecision);
846 string res = inst.IntegerToString (format, fp);
847 inst.Release();
848 return res;
851 public static string NumberToString (string format, ulong value, IFormatProvider fp)
853 NumberFormatter inst = GetInstance();
854 inst.Init (format, value);
855 string res = inst.IntegerToString (format, fp);
856 inst.Release();
857 return res;
860 public static string NumberToString (string format, long value, IFormatProvider fp)
862 NumberFormatter inst = GetInstance();
863 inst.Init (format, value);
864 string res = inst.IntegerToString (format, fp);
865 inst.Release();
866 return res;
869 public static string NumberToString (string format, float value, IFormatProvider fp)
871 NumberFormatter inst = GetInstance();
872 inst.Init (format, value, SingleDefPrecision);
873 NumberFormatInfo nfi = inst.GetNumberFormatInstance (fp);
874 string res;
875 if (inst._NaN)
876 res = nfi.NaNSymbol;
877 else if (inst._infinity)
878 if (inst._positive)
879 res = nfi.PositiveInfinitySymbol;
880 else
881 res = nfi.NegativeInfinitySymbol;
882 else if (inst._specifier == 'R')
883 res = inst.FormatRoundtrip (value, nfi);
884 else
885 res = inst.NumberToString (format, nfi);
886 inst.Release();
887 return res;
890 public static string NumberToString (string format, double value, IFormatProvider fp)
892 NumberFormatter inst = GetInstance();
893 inst.Init (format, value, DoubleDefPrecision);
894 NumberFormatInfo nfi = inst.GetNumberFormatInstance (fp);
895 string res;
896 if (inst._NaN)
897 res = nfi.NaNSymbol;
898 else if (inst._infinity)
899 if (inst._positive)
900 res = nfi.PositiveInfinitySymbol;
901 else
902 res = nfi.NegativeInfinitySymbol;
903 else if (inst._specifier == 'R')
904 res = inst.FormatRoundtrip (value, nfi);
905 else
906 res = inst.NumberToString (format, nfi);
907 inst.Release();
908 return res;
911 public static string NumberToString (string format, decimal value, IFormatProvider fp)
913 NumberFormatter inst = GetInstance();
914 inst.Init (format, value);
915 string res = inst.NumberToString (format, inst.GetNumberFormatInstance (fp));
916 inst.Release();
917 return res;
920 public static string NumberToString (uint value, IFormatProvider fp)
922 if (value >= HundredMillion)
923 return NumberToString (null, value, fp);
925 NumberFormatter inst = GetInstance();
926 string res = inst.FastIntegerToString ((int)value, fp);
927 inst.Release();
928 return res;
931 public static string NumberToString (int value, IFormatProvider fp)
933 if (value >= HundredMillion || value <= -HundredMillion)
934 return NumberToString (null, value, fp);
936 NumberFormatter inst = GetInstance();
937 string res = inst.FastIntegerToString (value, fp);
938 inst.Release();
939 return res;
942 public static string NumberToString (ulong value, IFormatProvider fp)
944 if (value >= HundredMillion)
945 return NumberToString (null, value, fp);
947 NumberFormatter inst = GetInstance();
948 string res = inst.FastIntegerToString ((int)value, fp);
949 inst.Release();
950 return res;
953 public static string NumberToString (long value, IFormatProvider fp)
955 if (value >= HundredMillion || value <= -HundredMillion)
956 return NumberToString (null, value, fp);
958 NumberFormatter inst = GetInstance();
959 string res = inst.FastIntegerToString ((int)value, fp);
960 inst.Release();
961 return res;
964 public static string NumberToString (float value, IFormatProvider fp)
966 NumberFormatter inst = GetInstance();
967 inst.Init (null, value, SingleDefPrecision);
968 NumberFormatInfo nfi = inst.GetNumberFormatInstance (fp);
969 string res;
970 if (inst._NaN)
971 res = nfi.NaNSymbol;
972 else if (inst._infinity)
973 if (inst._positive)
974 res = nfi.PositiveInfinitySymbol;
975 else
976 res = nfi.NegativeInfinitySymbol;
977 else
978 res = inst.FormatGeneral (-1, nfi);
979 inst.Release();
980 return res;
983 public static string NumberToString (double value, IFormatProvider fp)
985 NumberFormatter inst = GetInstance();
986 NumberFormatInfo nfi = inst.GetNumberFormatInstance (fp);
987 inst.Init (null, value, DoubleDefPrecision);
988 string res;
989 if (inst._NaN)
990 res = nfi.NaNSymbol;
991 else if (inst._infinity)
992 if (inst._positive)
993 res = nfi.PositiveInfinitySymbol;
994 else
995 res = nfi.NegativeInfinitySymbol;
996 else
997 res = inst.FormatGeneral (-1, nfi);
998 inst.Release();
999 return res;
1002 private string FastIntegerToString (int value, IFormatProvider fp)
1004 if (value < 0) {
1005 string sign = GetNumberFormatInstance(fp).NegativeSign;
1006 ResetCharBuf (8 + sign.Length);
1007 value = -value;
1008 Append (sign);
1010 else
1011 ResetCharBuf (8);
1013 if (value >= 10000) {
1014 int v = value / 10000;
1015 FastAppendDigits (v, false);
1016 FastAppendDigits (value - v * 10000, true);
1018 else
1019 FastAppendDigits (value, false);
1021 return new string (_cbuf, 0, _ind);
1024 private string IntegerToString (string format, IFormatProvider fp)
1026 NumberFormatInfo nfi = GetNumberFormatInstance (fp);
1027 switch (_specifier) {
1028 case 'C':
1029 return FormatCurrency (_precision, nfi);
1030 case 'D':
1031 return FormatDecimal (_precision, nfi);
1032 case 'E':
1033 return FormatExponential (_precision, nfi);
1034 case 'F':
1035 return FormatFixedPoint (_precision, nfi);
1036 case 'G':
1037 if (_precision <= 0)
1038 return FormatDecimal (-1, nfi);
1039 return FormatGeneral (_precision, nfi);
1040 case 'N':
1041 return FormatNumber (_precision, nfi);
1042 case 'P':
1043 return FormatPercent (_precision, nfi);
1044 case 'X':
1045 return FormatHexadecimal (_precision);
1046 default:
1047 if (_isCustomFormat)
1048 return FormatCustom (format, nfi);
1049 throw new FormatException ("The specified format '" + format + "' is invalid");
1053 private string NumberToString (string format, NumberFormatInfo nfi)
1055 switch (_specifier) {
1056 case 'C':
1057 return FormatCurrency (_precision, nfi);
1058 case 'E':
1059 return FormatExponential (_precision, nfi);
1060 case 'F':
1061 return FormatFixedPoint (_precision, nfi);
1062 case 'G':
1063 return FormatGeneral (_precision, nfi);
1064 case 'N':
1065 return FormatNumber (_precision, nfi);
1066 case 'P':
1067 return FormatPercent (_precision, nfi);
1068 case 'X':
1069 default:
1070 if (_isCustomFormat)
1071 return FormatCustom (format, nfi);
1072 throw new FormatException ("The specified format '" + format + "' is invalid");
1076 public string FormatCurrency (int precision, NumberFormatInfo nfi)
1078 precision = (precision >= 0 ? precision : nfi.CurrencyDecimalDigits);
1079 RoundDecimal (precision);
1080 ResetCharBuf (IntegerDigits * 2 + precision * 2 + 16);
1082 if (_positive) {
1083 switch (nfi.CurrencyPositivePattern) {
1084 case 0:
1085 Append (nfi.CurrencySymbol);
1086 break;
1087 case 2:
1088 Append (nfi.CurrencySymbol);
1089 Append (' ');
1090 break;
1093 else {
1094 switch (nfi.CurrencyNegativePattern) {
1095 case 0:
1096 Append ('(');
1097 Append (nfi.CurrencySymbol);
1098 break;
1099 case 1:
1100 Append (nfi.NegativeSign);
1101 Append (nfi.CurrencySymbol);
1102 break;
1103 case 2:
1104 Append (nfi.CurrencySymbol);
1105 Append (nfi.NegativeSign);
1106 break;
1107 case 3:
1108 Append (nfi.CurrencySymbol);
1109 break;
1110 case 4:
1111 Append ('(');
1112 break;
1113 case 5:
1114 Append (nfi.NegativeSign);
1115 break;
1116 case 8:
1117 Append (nfi.NegativeSign);
1118 break;
1119 case 9:
1120 Append (nfi.NegativeSign);
1121 Append (nfi.CurrencySymbol);
1122 Append (' ');
1123 break;
1124 case 11:
1125 Append (nfi.CurrencySymbol);
1126 Append (' ');
1127 break;
1128 case 12:
1129 Append (nfi.CurrencySymbol);
1130 Append (' ');
1131 Append (nfi.NegativeSign);
1132 break;
1133 case 14:
1134 Append ('(');
1135 Append (nfi.CurrencySymbol);
1136 Append (' ');
1137 break;
1138 case 15:
1139 Append ('(');
1140 break;
1144 AppendIntegerStringWithGroupSeparator (nfi.RawCurrencyGroupSizes, nfi.CurrencyGroupSeparator);
1146 if (precision > 0) {
1147 Append (nfi.CurrencyDecimalSeparator);
1148 AppendDecimalString (precision);
1151 if (_positive) {
1152 switch (nfi.CurrencyPositivePattern) {
1153 case 1:
1154 Append (nfi.CurrencySymbol);
1155 break;
1156 case 3:
1157 Append (' ');
1158 Append (nfi.CurrencySymbol);
1159 break;
1162 else {
1163 switch (nfi.CurrencyNegativePattern) {
1164 case 0:
1165 Append (')');
1166 break;
1167 case 3:
1168 Append (nfi.NegativeSign);
1169 break;
1170 case 4:
1171 Append (nfi.CurrencySymbol);
1172 Append (')');
1173 break;
1174 case 5:
1175 Append (nfi.CurrencySymbol);
1176 break;
1177 case 6:
1178 Append (nfi.NegativeSign);
1179 Append (nfi.CurrencySymbol);
1180 break;
1181 case 7:
1182 Append (nfi.CurrencySymbol);
1183 Append (nfi.NegativeSign);
1184 break;
1185 case 8:
1186 Append (' ');
1187 Append (nfi.CurrencySymbol);
1188 break;
1189 case 10:
1190 Append (' ');
1191 Append (nfi.CurrencySymbol);
1192 Append (nfi.NegativeSign);
1193 break;
1194 case 11:
1195 Append (nfi.NegativeSign);
1196 break;
1197 case 13:
1198 Append (nfi.NegativeSign);
1199 Append (' ');
1200 Append (nfi.CurrencySymbol);
1201 break;
1202 case 14:
1203 Append (')');
1204 break;
1205 case 15:
1206 Append (' ');
1207 Append (nfi.CurrencySymbol);
1208 Append (')');
1209 break;
1213 return new string (_cbuf, 0, _ind);
1216 private string FormatDecimal (int precision, NumberFormatInfo nfi)
1218 if (precision < _digitsLen)
1219 precision = _digitsLen;
1220 if (precision == 0)
1221 return "0";
1223 ResetCharBuf (precision + 1);
1224 if (!_positive)
1225 Append (nfi.NegativeSign);
1226 AppendDigits (0, precision);
1228 return new string (_cbuf, 0, _ind);
1231 #if UNSAFE_TABLES // No unsafe code under TARGET_JVM
1232 unsafe
1233 #endif
1234 private string FormatHexadecimal (int precision)
1236 int size = Math.Max (precision, _decPointPos);
1237 #if UNSAFE_TABLES
1238 char* digits = _specifierIsUpper ? DigitUpperTable : DigitLowerTable;
1239 #else
1240 char[] digits = _specifierIsUpper ? DigitUpperTable : DigitLowerTable;
1241 #endif
1242 ResetCharBuf (size);
1243 _ind = size;
1244 ulong val = _val1 | ((ulong)_val2 << 32);
1245 while (size > 0) {
1246 _cbuf [--size] = digits [val & 0xf];
1247 val >>= 4;
1249 return new string (_cbuf, 0, _ind);
1252 public string FormatFixedPoint (int precision, NumberFormatInfo nfi)
1254 if (precision == -1)
1255 precision = nfi.NumberDecimalDigits;
1257 RoundDecimal (precision);
1259 ResetCharBuf (IntegerDigits + precision + 2);
1261 if (!_positive)
1262 Append (nfi.NegativeSign);
1264 AppendIntegerString (IntegerDigits);
1266 if (precision > 0) {
1267 Append (nfi.NumberDecimalSeparator);
1268 AppendDecimalString (precision);
1271 return new string (_cbuf, 0, _ind);
1274 private string FormatRoundtrip (double origval, NumberFormatInfo nfi)
1276 NumberFormatter nfc = GetClone ();
1277 if (origval >= MinRoundtripVal && origval <= MaxRoundtripVal) {
1278 string shortRep = FormatGeneral (_defPrecision, nfi);
1279 if (origval == Double.Parse (shortRep, nfi))
1280 return shortRep;
1282 return nfc.FormatGeneral (_defPrecision + 2, nfi);
1285 private string FormatRoundtrip (float origval, NumberFormatInfo nfi)
1287 NumberFormatter nfc = GetClone ();
1288 string shortRep = FormatGeneral (_defPrecision, nfi);
1289 // Check roundtrip only for "normal" double values.
1290 if (origval == Single.Parse (shortRep, nfi))
1291 return shortRep;
1292 return nfc.FormatGeneral (_defPrecision + 2, nfi);
1295 private string FormatGeneral (int precision, NumberFormatInfo nfi)
1297 bool enableExp;
1298 if (precision == -1) {
1299 enableExp = IsFloatingSource;
1300 precision = _defPrecision;
1302 else {
1303 enableExp = true;
1304 if (precision == 0)
1305 precision = _defPrecision;
1306 RoundPos (precision);
1309 int intDigits = _decPointPos;
1310 int digits = _digitsLen;
1311 int decDigits = digits - intDigits;
1313 if ((intDigits > precision || intDigits <= -4) && enableExp)
1314 return FormatExponential (digits - 1, nfi, 2);
1316 if (decDigits < 0)
1317 decDigits = 0;
1318 if (intDigits < 0)
1319 intDigits = 0;
1320 ResetCharBuf (decDigits + intDigits + 3);
1322 if (!_positive)
1323 Append (nfi.NegativeSign);
1325 if (intDigits == 0)
1326 Append ('0');
1327 else
1328 AppendDigits (digits - intDigits, digits);
1330 if (decDigits > 0) {
1331 Append (nfi.NumberDecimalSeparator);
1332 AppendDigits (0, decDigits);
1335 return new string (_cbuf, 0, _ind);
1338 public string FormatNumber (int precision, NumberFormatInfo nfi)
1340 precision = (precision >= 0 ? precision : nfi.NumberDecimalDigits);
1341 ResetCharBuf (IntegerDigits * 3 + precision);
1342 RoundDecimal (precision);
1344 if (!_positive) {
1345 switch (nfi.NumberNegativePattern) {
1346 case 0:
1347 Append ('(');
1348 break;
1349 case 1:
1350 Append (nfi.NegativeSign);
1351 break;
1352 case 2:
1353 Append (nfi.NegativeSign);
1354 Append (' ');
1355 break;
1359 AppendIntegerStringWithGroupSeparator (nfi.RawNumberGroupSizes, nfi.NumberGroupSeparator);
1361 if (precision > 0) {
1362 Append (nfi.NumberDecimalSeparator);
1363 AppendDecimalString (precision);
1366 if (!_positive) {
1367 switch (nfi.NumberNegativePattern) {
1368 case 0:
1369 Append (')');
1370 break;
1371 case 3:
1372 Append (nfi.NegativeSign);
1373 break;
1374 case 4:
1375 Append (' ');
1376 Append (nfi.NegativeSign);
1377 break;
1381 return new string (_cbuf, 0, _ind);
1384 public string FormatPercent (int precision, NumberFormatInfo nfi)
1386 precision = (precision >= 0 ? precision : nfi.PercentDecimalDigits);
1387 Multiply10(2);
1388 RoundDecimal (precision);
1389 ResetCharBuf (IntegerDigits * 2 + precision + 16);
1391 if (_positive) {
1392 if (nfi.PercentPositivePattern == 2)
1393 Append (nfi.PercentSymbol);
1395 else {
1396 switch (nfi.PercentNegativePattern) {
1397 case 0:
1398 Append (nfi.NegativeSign);
1399 break;
1400 case 1:
1401 Append (nfi.NegativeSign);
1402 break;
1403 case 2:
1404 Append (nfi.NegativeSign);
1405 Append (nfi.PercentSymbol);
1406 break;
1410 AppendIntegerStringWithGroupSeparator (nfi.RawPercentGroupSizes, nfi.PercentGroupSeparator);
1412 if (precision > 0) {
1413 Append (nfi.PercentDecimalSeparator);
1414 AppendDecimalString (precision);
1417 if (_positive) {
1418 switch (nfi.PercentPositivePattern) {
1419 case 0:
1420 Append (' ');
1421 Append (nfi.PercentSymbol);
1422 break;
1423 case 1:
1424 Append (nfi.PercentSymbol);
1425 break;
1428 else {
1429 switch (nfi.PercentNegativePattern) {
1430 case 0:
1431 Append (' ');
1432 Append (nfi.PercentSymbol);
1433 break;
1434 case 1:
1435 Append (nfi.PercentSymbol);
1436 break;
1440 return new string (_cbuf, 0, _ind);
1443 public string FormatExponential (int precision, NumberFormatInfo nfi)
1445 if (precision == -1)
1446 precision = DefaultExpPrecision;
1448 RoundPos (precision + 1);
1449 return FormatExponential (precision, nfi, 3);
1452 private string FormatExponential (int precision, NumberFormatInfo nfi, int expDigits)
1454 int decDigits = _decPointPos;
1455 int digits = _digitsLen;
1456 int exponent = decDigits - 1;
1457 decDigits = _decPointPos = 1;
1459 ResetCharBuf (precision + 8);
1461 if (!_positive)
1462 Append (nfi.NegativeSign);
1464 AppendOneDigit (digits - 1);
1466 if (precision > 0) {
1467 Append (nfi.NumberDecimalSeparator);
1468 AppendDigits (digits - precision - 1, digits - _decPointPos);
1471 AppendExponent (nfi, exponent, expDigits);
1473 return new string (_cbuf, 0, _ind);
1476 public string FormatCustom (string format, NumberFormatInfo nfi)
1478 bool p = _positive;
1479 int offset = 0;
1480 int length = 0;
1481 CustomInfo.GetActiveSection (format, ref p, IsZero, ref offset, ref length);
1482 if (length == 0)
1483 return _positive ? string.Empty : nfi.NegativeSign;
1484 _positive = p;
1486 CustomInfo info = CustomInfo.Parse (format, offset, length, nfi);
1487 #if false
1488 Console.WriteLine ("Format : {0}",format);
1489 Console.WriteLine ("DecimalDigits : {0}",info.DecimalDigits);
1490 Console.WriteLine ("DecimalPointPos : {0}",info.DecimalPointPos);
1491 Console.WriteLine ("DecimalTailSharpDigits : {0}",info.DecimalTailSharpDigits);
1492 Console.WriteLine ("IntegerDigits : {0}",info.IntegerDigits);
1493 Console.WriteLine ("IntegerHeadSharpDigits : {0}",info.IntegerHeadSharpDigits);
1494 Console.WriteLine ("IntegerHeadPos : {0}",info.IntegerHeadPos);
1495 Console.WriteLine ("UseExponent : {0}",info.UseExponent);
1496 Console.WriteLine ("ExponentDigits : {0}",info.ExponentDigits);
1497 Console.WriteLine ("ExponentTailSharpDigits : {0}",info.ExponentTailSharpDigits);
1498 Console.WriteLine ("ExponentNegativeSignOnly : {0}",info.ExponentNegativeSignOnly);
1499 Console.WriteLine ("DividePlaces : {0}",info.DividePlaces);
1500 Console.WriteLine ("Percents : {0}",info.Percents);
1501 Console.WriteLine ("Permilles : {0}",info.Permilles);
1502 #endif
1503 StringBuilder sb_int = new StringBuilder (info.IntegerDigits * 2);
1504 StringBuilder sb_dec = new StringBuilder (info.DecimalDigits * 2);
1505 StringBuilder sb_exp = (info.UseExponent ? new StringBuilder (info.ExponentDigits * 2) : null);
1507 int diff = 0;
1508 if (info.Percents > 0)
1509 Multiply10(2 * info.Percents);
1510 if (info.Permilles > 0)
1511 Multiply10(3 * info.Permilles);
1512 if (info.DividePlaces > 0)
1513 Divide10(info.DividePlaces);
1515 bool expPositive = true;
1516 if (info.UseExponent && (info.DecimalDigits > 0 || info.IntegerDigits > 0)) {
1517 if (!IsZero) {
1518 RoundPos (info.DecimalDigits + info.IntegerDigits);
1519 diff -= _decPointPos - info.IntegerDigits;
1520 _decPointPos = info.IntegerDigits;
1523 expPositive = diff <= 0;
1524 AppendNonNegativeNumber (sb_exp, diff < 0 ? -diff : diff);
1526 else
1527 RoundDecimal (info.DecimalDigits);
1529 if (info.IntegerDigits != 0 || !IsZeroInteger)
1530 AppendIntegerString (IntegerDigits, sb_int);
1532 AppendDecimalString (DecimalDigits, sb_dec);
1534 if (info.UseExponent) {
1535 if (info.DecimalDigits <= 0 && info.IntegerDigits <= 0)
1536 _positive = true;
1538 if (sb_int.Length < info.IntegerDigits)
1539 sb_int.Insert (0, "0", info.IntegerDigits - sb_int.Length);
1541 while (sb_exp.Length < info.ExponentDigits - info.ExponentTailSharpDigits)
1542 sb_exp.Insert (0, '0');
1544 if (expPositive && !info.ExponentNegativeSignOnly)
1545 sb_exp.Insert (0, nfi.PositiveSign);
1546 else if (!expPositive)
1547 sb_exp.Insert (0, nfi.NegativeSign);
1549 else {
1550 if (sb_int.Length < info.IntegerDigits - info.IntegerHeadSharpDigits)
1551 sb_int.Insert (0, "0", info.IntegerDigits - info.IntegerHeadSharpDigits - sb_int.Length);
1552 if (info.IntegerDigits == info.IntegerHeadSharpDigits && IsZeroOnly (sb_int))
1553 sb_int.Remove (0, sb_int.Length);
1556 ZeroTrimEnd (sb_dec, true);
1557 while (sb_dec.Length < info.DecimalDigits - info.DecimalTailSharpDigits)
1558 sb_dec.Append ('0');
1559 if (sb_dec.Length > info.DecimalDigits)
1560 sb_dec.Remove (info.DecimalDigits, sb_dec.Length - info.DecimalDigits);
1562 return info.Format (format, offset, length, nfi, _positive, sb_int, sb_dec, sb_exp);
1564 #endregion public number formatting methods
1566 #region StringBuilder formatting helpers
1568 private static void ZeroTrimEnd (StringBuilder sb, bool canEmpty)
1570 int len = 0;
1571 for (int i = sb.Length - 1; (canEmpty ? i >= 0 : i > 0); i--) {
1572 if (sb [i] != '0')
1573 break;
1574 len++;
1577 if (len > 0)
1578 sb.Remove (sb.Length - len, len);
1581 private static bool IsZeroOnly (StringBuilder sb)
1583 for (int i = 0; i < sb.Length; i++)
1584 if (char.IsDigit (sb [i]) && sb [i] != '0')
1585 return false;
1586 return true;
1589 private static void AppendNonNegativeNumber (StringBuilder sb, int v)
1591 if (v < 0)
1592 throw new ArgumentException ();
1594 int i = ScaleOrder (v) - 1;
1595 do {
1596 int n = v / (int)GetTenPowerOf (i);
1597 sb.Append ((char)('0' | n));
1598 v -= (int)GetTenPowerOf (i--) * n;
1599 } while (i >= 0);
1602 #endregion StringBuilder formatting helpers
1604 #region Append helpers
1606 private void AppendIntegerString (int minLength, StringBuilder sb)
1608 if (_decPointPos <= 0) {
1609 sb.Append ('0', minLength);
1610 return;
1613 if (_decPointPos < minLength)
1614 sb.Append ('0', minLength - _decPointPos);
1616 AppendDigits (_digitsLen - _decPointPos, _digitsLen, sb);
1619 private void AppendIntegerString (int minLength)
1621 if (_decPointPos <= 0) {
1622 Append ('0', minLength);
1623 return;
1626 if (_decPointPos < minLength)
1627 Append ('0', minLength - _decPointPos);
1629 AppendDigits (_digitsLen - _decPointPos, _digitsLen);
1632 private void AppendDecimalString (int precision, StringBuilder sb)
1634 AppendDigits (_digitsLen - precision - _decPointPos, _digitsLen - _decPointPos, sb);
1637 private void AppendDecimalString (int precision)
1639 AppendDigits (_digitsLen - precision - _decPointPos, _digitsLen - _decPointPos);
1642 private void AppendIntegerStringWithGroupSeparator (int[] groups, string groupSeparator)
1644 if (IsZeroInteger) {
1645 Append ('0');
1646 return;
1649 int total = 0;
1650 int groupIndex = 0;
1651 for (int i = 0; i < groups.Length; i++) {
1652 total += groups [i];
1653 if (total <= _decPointPos)
1654 groupIndex = i;
1655 else
1656 break;
1659 if (groups.Length > 0 && total > 0) {
1660 int counter;
1661 int groupSize = groups [groupIndex];
1662 int fraction = _decPointPos > total ? _decPointPos - total : 0;
1663 if (groupSize == 0) {
1664 while (groupIndex >= 0 && groups [groupIndex] == 0)
1665 groupIndex--;
1667 groupSize = fraction > 0 ? fraction : groups [groupIndex];
1669 if (fraction == 0)
1670 counter = groupSize;
1671 else {
1672 groupIndex += fraction / groupSize;
1673 counter = fraction % groupSize;
1674 if (counter == 0)
1675 counter = groupSize;
1676 else
1677 groupIndex++;
1680 for (int i = 0; ;) {
1681 if ((_decPointPos - i) <= counter || counter == 0) {
1682 AppendDigits (_digitsLen - _decPointPos, _digitsLen - i);
1683 break;
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;
1693 else {
1694 AppendDigits (_digitsLen - _decPointPos, _digitsLen);
1698 // minDigits is in the range 1..3
1699 private void AppendExponent (NumberFormatInfo nfi, int exponent, int minDigits)
1701 if (_specifierIsUpper || _specifier == 'R')
1702 Append ('E');
1703 else
1704 Append ('e');
1706 if (exponent >= 0)
1707 Append (nfi.PositiveSign);
1708 else {
1709 Append (nfi.NegativeSign);
1710 exponent = -exponent;
1713 if (exponent == 0)
1714 Append ('0', minDigits);
1715 else if (exponent < 10) {
1716 Append ('0', minDigits - 1);
1717 Append ((char)('0' | exponent));
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)));
1728 private void AppendOneDigit (int start)
1730 if (_ind == _cbuf.Length)
1731 Resize (_ind + 10);
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);
1751 #if UNSAFE_TABLES // No unsafe code under TARGET_JVM
1752 unsafe
1753 #endif
1754 private void FastAppendDigits (int val, bool force)
1756 int i = _ind;
1757 int digits;
1758 if (force || val >= 100) {
1759 int v = (val * 5243) >> 19;
1760 digits = DecHexDigits [v];
1761 if (force || val >= 1000)
1762 _cbuf [i++] = (char)('0' | digits >> 4);
1763 _cbuf [i++] = (char)('0' | (digits & 0xf));
1764 digits = DecHexDigits [val - v * 100];
1766 else
1767 digits = DecHexDigits [val];
1769 if (force || val >= 10)
1770 _cbuf [i++] = (char)('0' | digits >> 4);
1771 _cbuf [i++] = (char)('0' | (digits & 0xf));
1772 _ind = i;
1775 private void AppendDigits (int start, int end)
1777 if (start >= end)
1778 return;
1780 int i = _ind + (end - start);
1781 if (i > _cbuf.Length)
1782 Resize (i + 10);
1783 _ind = i;
1785 end += _offset;
1786 start += _offset;
1788 for (int next = start + 8 - (start & 0x7); ; start = next, next += 8) {
1789 uint v;
1790 if (next == 8)
1791 v = _val1;
1792 else if (next == 16)
1793 v = _val2;
1794 else if (next == 24)
1795 v = _val3;
1796 else if (next == 32)
1797 v = _val4;
1798 else
1799 v = 0;
1800 v >>= (start & 0x7) << 2;
1801 if (next > end)
1802 next = end;
1804 _cbuf [--i] = (char)('0' | v & 0xf);
1805 switch (next - start) {
1806 case 8:
1807 _cbuf [--i] = (char)('0' | (v >>= 4) & 0xf);
1808 goto case 7;
1809 case 7:
1810 _cbuf [--i] = (char)('0' | (v >>= 4) & 0xf);
1811 goto case 6;
1812 case 6:
1813 _cbuf [--i] = (char)('0' | (v >>= 4) & 0xf);
1814 goto case 5;
1815 case 5:
1816 _cbuf [--i] = (char)('0' | (v >>= 4) & 0xf);
1817 goto case 4;
1818 case 4:
1819 _cbuf [--i] = (char)('0' | (v >>= 4) & 0xf);
1820 goto case 3;
1821 case 3:
1822 _cbuf [--i] = (char)('0' | (v >>= 4) & 0xf);
1823 goto case 2;
1824 case 2:
1825 _cbuf [--i] = (char)('0' | (v >>= 4) & 0xf);
1826 goto case 1;
1827 case 1:
1828 if (next == end)
1829 return;
1830 continue;
1835 private void AppendDigits (int start, int end, StringBuilder sb)
1837 if (start >= end)
1838 return;
1840 int i = sb.Length + (end - start);
1841 sb.Length = i;
1843 end += _offset;
1844 start += _offset;
1846 for (int next = start + 8 - (start & 0x7); ; start = next, next += 8) {
1847 uint v;
1848 if (next == 8)
1849 v = _val1;
1850 else if (next == 16)
1851 v = _val2;
1852 else if (next == 24)
1853 v = _val3;
1854 else if (next == 32)
1855 v = _val4;
1856 else
1857 v = 0;
1858 v >>= (start & 0x7) << 2;
1859 if (next > end)
1860 next = end;
1861 sb [--i] = (char)('0' | v & 0xf);
1862 switch (next - start) {
1863 case 8:
1864 sb [--i] = (char)('0' | (v >>= 4) & 0xf);
1865 goto case 7;
1866 case 7:
1867 sb [--i] = (char)('0' | (v >>= 4) & 0xf);
1868 goto case 6;
1869 case 6:
1870 sb [--i] = (char)('0' | (v >>= 4) & 0xf);
1871 goto case 5;
1872 case 5:
1873 sb [--i] = (char)('0' | (v >>= 4) & 0xf);
1874 goto case 4;
1875 case 4:
1876 sb [--i] = (char)('0' | (v >>= 4) & 0xf);
1877 goto case 3;
1878 case 3:
1879 sb [--i] = (char)('0' | (v >>= 4) & 0xf);
1880 goto case 2;
1881 case 2:
1882 sb [--i] = (char)('0' | (v >>= 4) & 0xf);
1883 goto case 1;
1884 case 1:
1885 if (next == end)
1886 return;
1887 continue;
1892 #endregion Append helpers
1894 #region others
1896 private void Multiply10(int count)
1898 if (count <= 0 || _digitsLen == 0)
1899 return;
1901 _decPointPos += count;
1904 private void Divide10(int count)
1906 if (count <= 0 || _digitsLen == 0)
1907 return;
1909 _decPointPos -= count;
1912 private NumberFormatter GetClone ()
1914 return (NumberFormatter)this.MemberwiseClone ();
1917 #endregion others
1919 #region custom
1921 private class CustomInfo
1923 public bool UseGroup = false;
1924 public int DecimalDigits = 0;
1925 public int DecimalPointPos = -1;
1926 public int DecimalTailSharpDigits = 0;
1927 public int IntegerDigits = 0;
1928 public int IntegerHeadSharpDigits = 0;
1929 public int IntegerHeadPos = 0;
1930 public bool UseExponent = false;
1931 public int ExponentDigits = 0;
1932 public int ExponentTailSharpDigits = 0;
1933 public bool ExponentNegativeSignOnly = true;
1934 public int DividePlaces = 0;
1935 public int Percents = 0;
1936 public int Permilles = 0;
1938 public static void GetActiveSection (string format, ref bool positive, bool zero, ref int offset, ref int length)
1940 int[] lens = new int [3];
1941 int index = 0;
1942 int lastPos = 0;
1943 char literal = '\0';
1944 for (int i = 0; i < format.Length; i++) {
1945 char c = format [i];
1947 if (c == literal || (literal == '\0' && (c == '\"' || c == '\''))) {
1948 if (literal == '\0')
1949 literal = c;
1950 else
1951 literal = '\0';
1952 continue;
1955 if (literal == '\0' && format [i] == ';' && (i == 0 || format [i - 1] != '\\')) {
1956 lens [index++] = i - lastPos;
1957 lastPos = i + 1;
1958 if (index == 3)
1959 break;
1963 if (index == 0) {
1964 offset = 0;
1965 length = format.Length;
1966 return;
1968 if (index == 1) {
1969 if (positive || zero) {
1970 offset = 0;
1971 length = lens [0];
1972 return;
1974 if (lens [0] + 1 < format.Length) {
1975 positive = true;
1976 offset = lens [0] + 1;
1977 length = format.Length - offset;
1978 return;
1980 else {
1981 offset = 0;
1982 length = lens [0];
1983 return;
1986 if (index == 2) {
1987 if (zero) {
1988 offset = lens [0] + lens [1] + 2;
1989 length = format.Length - offset;
1990 return;
1992 if (positive) {
1993 offset = 0;
1994 length = lens [0];
1995 return;
1997 if (lens [1] > 0) {
1998 positive = true;
1999 offset = lens [0] + 1;
2000 length = lens [1];
2001 return;
2003 else {
2004 offset = 0;
2005 length = lens [0];
2006 return;
2009 if (index == 3) {
2010 if (zero) {
2011 offset = lens [0] + lens [1] + 2;
2012 length = lens [2];
2013 return;
2015 if (positive) {
2016 offset = 0;
2017 length = lens [0];
2018 return;
2020 if (lens [1] > 0) {
2021 positive = true;
2022 offset = lens [0] + 1;
2023 length = lens [1];
2024 return;
2026 else {
2027 offset = 0;
2028 length = lens [0];
2029 return;
2033 throw new ArgumentException ();
2036 public static CustomInfo Parse (string format, int offset, int length, NumberFormatInfo nfi)
2038 char literal = '\0';
2039 bool integerArea = true;
2040 bool decimalArea = false;
2041 bool exponentArea = false;
2042 bool sharpContinues = true;
2044 CustomInfo info = new CustomInfo ();
2045 int groupSeparatorCounter = 0;
2047 for (int i = offset; i - offset < length; i++) {
2048 char c = format [i];
2050 if (c == literal && c != '\0') {
2051 literal = '\0';
2052 continue;
2054 if (literal != '\0')
2055 continue;
2057 if (exponentArea && (c != '\0' && c != '0' && c != '#')) {
2058 exponentArea = false;
2059 integerArea = (info.DecimalPointPos < 0);
2060 decimalArea = !integerArea;
2061 i--;
2062 continue;
2065 switch (c) {
2066 case '\\':
2067 i++;
2068 continue;
2069 case '\'':
2070 case '\"':
2071 if (c == '\"' || c == '\'') {
2072 literal = c;
2074 continue;
2075 case '#':
2076 if (sharpContinues && integerArea)
2077 info.IntegerHeadSharpDigits++;
2078 else if (decimalArea)
2079 info.DecimalTailSharpDigits++;
2080 else if (exponentArea)
2081 info.ExponentTailSharpDigits++;
2083 goto case '0';
2084 case '0':
2085 if (c != '#') {
2086 sharpContinues = false;
2087 if (decimalArea)
2088 info.DecimalTailSharpDigits = 0;
2089 else if (exponentArea)
2090 info.ExponentTailSharpDigits = 0;
2092 if (info.IntegerHeadPos == -1)
2093 info.IntegerHeadPos = i;
2095 if (integerArea) {
2096 info.IntegerDigits++;
2097 if (groupSeparatorCounter > 0)
2098 info.UseGroup = true;
2099 groupSeparatorCounter = 0;
2101 else if (decimalArea)
2102 info.DecimalDigits++;
2103 else if (exponentArea)
2104 info.ExponentDigits++;
2105 break;
2106 case 'e':
2107 case 'E':
2108 if (info.UseExponent)
2109 break;
2111 info.UseExponent = true;
2112 integerArea = false;
2113 decimalArea = false;
2114 exponentArea = true;
2115 if (i + 1 - offset < length) {
2116 char nc = format [i + 1];
2117 if (nc == '+')
2118 info.ExponentNegativeSignOnly = false;
2119 if (nc == '+' || nc == '-')
2120 i++;
2121 else if (nc != '0' && nc != '#') {
2122 info.UseExponent = false;
2123 if (info.DecimalPointPos < 0)
2124 integerArea = true;
2128 break;
2129 case '.':
2130 integerArea = false;
2131 decimalArea = true;
2132 exponentArea = false;
2133 if (info.DecimalPointPos == -1)
2134 info.DecimalPointPos = i;
2135 break;
2136 case '%':
2137 info.Percents++;
2138 break;
2139 case '\u2030':
2140 info.Permilles++;
2141 break;
2142 case ',':
2143 if (integerArea && info.IntegerDigits > 0)
2144 groupSeparatorCounter++;
2145 break;
2146 default:
2147 break;
2151 if (info.ExponentDigits == 0)
2152 info.UseExponent = false;
2153 else
2154 info.IntegerHeadSharpDigits = 0;
2156 if (info.DecimalDigits == 0)
2157 info.DecimalPointPos = -1;
2159 info.DividePlaces += groupSeparatorCounter * 3;
2161 return info;
2164 public string Format (string format, int offset, int length, NumberFormatInfo nfi, bool positive, StringBuilder sb_int, StringBuilder sb_dec, StringBuilder sb_exp)
2166 StringBuilder sb = new StringBuilder ();
2167 char literal = '\0';
2168 bool integerArea = true;
2169 bool decimalArea = false;
2170 int intSharpCounter = 0;
2171 int sb_int_index = 0;
2172 int sb_dec_index = 0;
2174 int[] groups = nfi.RawNumberGroupSizes;
2175 string groupSeparator = nfi.NumberGroupSeparator;
2176 int intLen = 0, total = 0, groupIndex = 0, counter = 0, groupSize = 0;
2177 if (UseGroup && groups.Length > 0) {
2178 intLen = sb_int.Length;
2179 for (int i = 0; i < groups.Length; i++) {
2180 total += groups [i];
2181 if (total <= intLen)
2182 groupIndex = i;
2184 groupSize = groups [groupIndex];
2185 int fraction = intLen > total ? intLen - total : 0;
2186 if (groupSize == 0) {
2187 while (groupIndex >= 0 && groups [groupIndex] == 0)
2188 groupIndex--;
2190 groupSize = fraction > 0 ? fraction : groups [groupIndex];
2192 if (fraction == 0)
2193 counter = groupSize;
2194 else {
2195 groupIndex += fraction / groupSize;
2196 counter = fraction % groupSize;
2197 if (counter == 0)
2198 counter = groupSize;
2199 else
2200 groupIndex++;
2203 else
2204 UseGroup = false;
2206 for (int i = offset; i - offset < length; i++) {
2207 char c = format [i];
2209 if (c == literal && c != '\0') {
2210 literal = '\0';
2211 continue;
2213 if (literal != '\0') {
2214 sb.Append (c);
2215 continue;
2218 switch (c) {
2219 case '\\':
2220 i++;
2221 if (i - offset < length)
2222 sb.Append (format [i]);
2223 continue;
2224 case '\'':
2225 case '\"':
2226 if (c == '\"' || c == '\'')
2227 literal = c;
2228 continue;
2229 case '#':
2230 goto case '0';
2231 case '0':
2232 if (integerArea) {
2233 intSharpCounter++;
2234 if (IntegerDigits - intSharpCounter < sb_int.Length + sb_int_index || c == '0')
2235 while (IntegerDigits - intSharpCounter + sb_int_index < sb_int.Length) {
2236 sb.Append (sb_int [sb_int_index++]);
2237 if (UseGroup && --intLen > 0 && --counter == 0) {
2238 sb.Append (groupSeparator);
2239 if (--groupIndex < groups.Length && groupIndex >= 0)
2240 groupSize = groups [groupIndex];
2241 counter = groupSize;
2244 break;
2246 else if (decimalArea) {
2247 if (sb_dec_index < sb_dec.Length)
2248 sb.Append (sb_dec [sb_dec_index++]);
2249 break;
2252 sb.Append (c);
2253 break;
2254 case 'e':
2255 case 'E':
2256 if (sb_exp == null || !UseExponent) {
2257 sb.Append (c);
2258 break;
2261 bool flag1 = true;
2262 bool flag2 = false;
2264 int q;
2265 for (q = i + 1; q - offset < length; q++) {
2266 if (format [q] == '0') {
2267 flag2 = true;
2268 continue;
2270 if (q == i + 1 && (format [q] == '+' || format [q] == '-'))
2271 continue;
2272 if (!flag2)
2273 flag1 = false;
2274 break;
2277 if (flag1) {
2278 i = q - 1;
2279 integerArea = (DecimalPointPos < 0);
2280 decimalArea = !integerArea;
2282 sb.Append (c);
2283 sb.Append (sb_exp);
2284 sb_exp = null;
2286 else
2287 sb.Append (c);
2289 break;
2290 case '.':
2291 if (DecimalPointPos == i) {
2292 if (DecimalDigits > 0) {
2293 while (sb_int_index < sb_int.Length)
2294 sb.Append (sb_int [sb_int_index++]);
2296 if (sb_dec.Length > 0)
2297 sb.Append (nfi.NumberDecimalSeparator);
2299 integerArea = false;
2300 decimalArea = true;
2301 break;
2302 case ',':
2303 break;
2304 case '%':
2305 sb.Append (nfi.PercentSymbol);
2306 break;
2307 case '\u2030':
2308 sb.Append (nfi.PerMilleSymbol);
2309 break;
2310 default:
2311 sb.Append (c);
2312 break;
2316 if (!positive)
2317 sb.Insert (0, nfi.NegativeSign);
2319 return sb.ToString ();
2323 #endregion