2 // System.NumberFormatter.cs
5 // Kazuki Oikawa (kazuki@panicode.com)
6 // Eyal Alaluf (eyala@mainsoft.com)
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:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
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.
37 using System.Globalization;
39 using System.Threading;
40 using System.Runtime.CompilerServices;
44 internal sealed partial class NumberFormatter
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;
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);
101 static long GetTenPowerOf(int i)
103 return TenPowersList [i];
105 #endregion Static Fields
109 private Thread _thread;
110 private NumberFormatInfo _nfi;
113 private bool _infinity;
114 private bool _isCustomFormat;
115 private bool _specifierIsUpper;
116 private bool _positive;
117 private char _specifier;
118 private int _precision;
119 private int _defPrecision;
121 private int _digitsLen;
122 private int _offset; // Represent the first digit offset.
123 private int _decPointPos;
125 // The following fields are a hexadeimal representation of the digits.
126 // For instance _val = 0x234 represents the digits '2', '3', '4'.
127 private uint _val1; // Digits 0 - 7.
128 private uint _val2; // Digits 8 - 15.
129 private uint _val3; // Digits 16 - 23.
130 private uint _val4; // Digits 23 - 31. Only needed for decimals.
134 #region Constructor Helpers
136 // Translate an unsigned int to hexadecimal digits.
137 // i.e. 123456789 is represented by _val1 = 0x23456789 and _val2 = 0x1
138 private void InitDecHexDigits (uint value)
140 if (value >= HundredMillion) {
141 int div1 = (int)(value / HundredMillion);
142 value -= HundredMillion * (uint)div1;
143 _val2 = FastToDecHex (div1);
145 _val1 = ToDecHex ((int)value);
148 // Translate an unsigned long to hexadecimal digits.
149 private void InitDecHexDigits (ulong value)
151 if (value >= HundredMillion) {
152 long div1 = (long)(value / HundredMillion);
153 value -= HundredMillion * (ulong)div1;
154 if (div1 >= HundredMillion) {
155 int div2 = (int)(div1 / HundredMillion);
156 div1 = div1 - div2 * (long)HundredMillion;
157 _val3 = ToDecHex (div2);
160 _val2 = ToDecHex ((int)(div1));
163 _val1 = ToDecHex ((int)value);
166 // Translate a decimal integer to hexadecimal digits.
167 // The decimal integer is 96 digits and its value is hi * 2^64 + lo.
168 // is the lower 64 bits.
169 private void InitDecHexDigits (uint hi, ulong lo)
172 InitDecHexDigits (lo); // Only the lower 64 bits matter.
176 // Compute (hi, lo) = (hi , lo) / HundredMillion.
177 uint divhi = hi / HundredMillion;
178 ulong remhi = hi - divhi * HundredMillion;
179 ulong divlo = lo / HundredMillion;
180 ulong remlo = lo - divlo * HundredMillion + remhi * ULongModHundredMillion;
182 lo = divlo + remhi * ULongDivHundredMillion;
183 divlo = remlo / HundredMillion;
184 remlo -= divlo * HundredMillion;
186 _val1 = ToDecHex ((int)remlo);
188 // Divide hi * 2 ^ 64 + lo by HundredMillion using the fact that
189 // hi < HundredMillion.
190 divlo = lo / HundredMillion;
191 remlo = lo - divlo * HundredMillion;
194 lo += hi * ULongDivHundredMillion;
195 remlo += hi * ULongModHundredMillion;
196 divlo = remlo / HundredMillion;
198 remlo -= divlo * HundredMillion;
200 _val2 = ToDecHex ((int)remlo);
202 // Now we are left with 64 bits store in lo.
203 if (lo >= HundredMillion) {
204 divlo = lo / HundredMillion;
205 lo -= divlo * HundredMillion;
206 _val4 = ToDecHex ((int)divlo);
208 _val3 = ToDecHex ((int)lo);
211 // Helper to translate an int in the range 0 .. 9999 to its
212 // Hexadecimal digits representation.
216 private static uint FastToDecHex (int val)
219 return (uint)DecHexDigits [val];
221 // Uses 2^19 (524288) to compute val / 100 for val < 10000.
222 int v = (val * 5243) >> 19;
223 return (uint)((DecHexDigits [v] << 8) | DecHexDigits [val - v * 100]);
226 // Helper to translate an int in the range 0 .. 99999999 to its
227 // Hexadecimal digits representation.
228 private static uint ToDecHex (int val)
234 res = FastToDecHex (v) << 16;
236 return res | FastToDecHex (val);
239 // Helper to count number of hexadecimal digits in a number.
240 private static int FastDecHexLen (int val)
247 else if (val < 0x1000)
253 private static int DecHexLen (uint val)
256 return FastDecHexLen ((int)val);
257 return 4 + FastDecHexLen ((int)(val >> 16));
260 // Count number of hexadecimal digits stored in _val1 .. _val4.
261 private int DecHexLen ()
264 return DecHexLen (_val4) + 24;
266 return DecHexLen (_val3) + 16;
268 return DecHexLen (_val2) + 8;
270 return DecHexLen (_val1);
275 // Helper to count the 10th scale (number of digits) in a number
276 private static int ScaleOrder (long hi)
278 for (int i = TenPowersListLength - 1; i >= 0; i--)
279 if (hi >= GetTenPowerOf (i))
284 // Compute the initial precision for rounding a floating number
285 // according to the used format.
286 int InitialFloatingPrecision ()
288 if (_specifier == 'R')
289 return _defPrecision + 2;
290 if (_precision < _defPrecision)
291 return _defPrecision;
292 if (_specifier == 'G')
293 return Math.Min (_defPrecision + 2, _precision);
294 if (_specifier == 'E')
295 return Math.Min (_defPrecision + 2, _precision + 1);
296 return _defPrecision;
299 // Parse the given format and extract the precision in it.
300 // Returns -1 for empty formats and -2 to indicate that the format
301 // is a custom format.
302 private static int ParsePrecision (string format)
305 for (int i = 1; i < format.Length; i++) {
306 int val = format [i] - '0';
307 precision = precision * 10 + val;
308 if (val < 0 || val > 9 || precision > 99)
314 #endregion Constructor Helpers
318 // Parse the given format and initialize the following fields:
319 // _isCustomFormat, _specifierIsUpper, _specifier & _precision.
320 public NumberFormatter (Thread current)
322 _cbuf = new char [0];
326 CurrentCulture = _thread.CurrentCulture;
329 private void Init (string format)
331 _val1 = _val2 = _val3 = _val4 = 0;
333 _NaN = _infinity = false;
334 _isCustomFormat = false;
335 _specifierIsUpper = true;
338 if (format == null || format.Length == 0) {
343 char specifier = format [0];
344 if (specifier >= 'a' && specifier <= 'z') {
345 specifier = (char)(specifier - 'a' + 'A');
346 _specifierIsUpper = false;
348 else if (specifier < 'A' || specifier > 'Z') {
349 _isCustomFormat = true;
353 _specifier = specifier;
354 if (format.Length > 1) {
355 _precision = ParsePrecision (format);
356 if (_precision == -2) { // Is it a custom format?
357 _isCustomFormat = true;
364 private void InitHex (ulong value)
366 switch (_defPrecision) {
367 case Int8DefPrecision: value = (byte) value; break;
368 case Int16DefPrecision: value = (ushort) value; break;
369 case Int32DefPrecision: value = (uint) value; break;
372 _val2 = (uint)(value >> 32);
373 _decPointPos = _digitsLen = DecHexLen ();
378 private void Init (string format, int value, int defPrecision)
381 _defPrecision = defPrecision;
382 _positive = value >= 0;
384 if (value == 0 || _specifier == 'X') {
385 InitHex ((ulong)value);
391 InitDecHexDigits ((uint)value);
392 _decPointPos = _digitsLen = DecHexLen ();
395 private void Init (string format, uint value, int defPrecision)
398 _defPrecision = defPrecision;
401 if (value == 0 || _specifier == 'X') {
406 InitDecHexDigits (value);
407 _decPointPos = _digitsLen = DecHexLen ();
410 private void Init (string format, long value)
413 _defPrecision = Int64DefPrecision;
414 _positive = value >= 0;
416 if (value == 0 || _specifier == 'X') {
417 InitHex ((ulong)value);
423 InitDecHexDigits ((ulong)value);
424 _decPointPos = _digitsLen = DecHexLen ();
427 private void Init (string format, ulong value)
430 _defPrecision = UInt64DefPrecision;
433 if (value == 0 || _specifier == 'X') {
434 InitHex ((ulong)value);
438 InitDecHexDigits (value);
439 _decPointPos = _digitsLen = DecHexLen ();
442 #if UNSAFE_TABLES // No unsafe code under TARGET_JVM
445 private void Init (string format, double value, int defPrecision)
449 _defPrecision = defPrecision;
450 long bits = BitConverter.DoubleToInt64Bits (value);
451 _positive = bits >= 0;
452 bits &= Int64.MaxValue;
460 int e = (int)(bits >> DoubleBitsExponentShift);
461 long m = bits & DoubleBitsMantissaMask;
462 if (e == DoubleBitsExponentMask) {
470 // We need 'm' to be large enough so we won't lose precision.
472 int scale = ScaleOrder (m);
473 if (scale < DoubleDefPrecision) {
474 expAdjust = scale - DoubleDefPrecision;
475 m *= GetTenPowerOf (-expAdjust);
479 m = (m + DoubleBitsMantissaMask + 1) * 10;
483 // multiply the mantissa by 10 ^ N
485 ulong hi = (ulong)m >> 32;
486 ulong lo2 = MantissaBitsTable [e];
487 ulong hi2 = lo2 >> 32;
489 ulong mm = hi * lo2 + lo * hi2 + ((lo * lo2) >> 32);
490 long res = (long)(hi * hi2 + (mm >> 32));
491 while (res < SeventeenDigitsThreshold) {
492 mm = (mm & UInt32.MaxValue) * 10;
493 res = res * 10 + (long)(mm >> 32);
496 if ((mm & 0x80000000) != 0)
499 int order = DoubleDefPrecision + 2;
500 _decPointPos = TensExponentTable [e] + expAdjust + order;
502 // Rescale 'res' to the initial precision (15-17 for doubles).
503 int initialPrecision = InitialFloatingPrecision ();
504 if (order > initialPrecision) {
505 long val = GetTenPowerOf (order - initialPrecision);
506 res = (res + (val >> 1)) / val;
507 order = initialPrecision;
509 if (res >= GetTenPowerOf (order)) {
514 InitDecHexDigits ((ulong)res);
515 _offset = CountTrailingZeros ();
516 _digitsLen = order - _offset;
519 private void Init (string format, decimal value)
522 _defPrecision = DecimalDefPrecision;
524 int[] bits = decimal.GetBits (value);
525 int scale = (bits [3] & DecimalBitsScaleMask) >> 16;
526 _positive = bits [3] >= 0;
527 if (bits [0] == 0 && bits [1] == 0 && bits [2] == 0) {
528 _decPointPos = -scale;
534 InitDecHexDigits ((uint)bits [2], ((ulong)bits [1] << 32) | (uint)bits [0]);
535 _digitsLen = DecHexLen ();
536 _decPointPos = _digitsLen - scale;
537 if (_precision != -1 || _specifier != 'G') {
538 _offset = CountTrailingZeros ();
539 _digitsLen -= _offset;
543 #endregion Constructors
545 #region Inner String Buffer
547 private char[] _cbuf;
550 private void ResetCharBuf (int size)
553 if (_cbuf.Length < size)
554 _cbuf = new char [size];
557 private void Resize (int len)
559 char[] newBuf = new char [len];
560 Array.Copy (_cbuf, newBuf, _ind);
564 private void Append (char c)
566 if (_ind == _cbuf.Length)
571 private void Append (char c, int cnt)
573 if (_ind + cnt > _cbuf.Length)
574 Resize (_ind + cnt + 10);
579 private void Append (string s)
582 if (_ind + slen > _cbuf.Length)
583 Resize (_ind + slen + 10);
584 for (int i = 0; i < slen; i++)
585 _cbuf [_ind++] = s [i];
588 #endregion Inner String Buffer
590 #region Helper properties
592 private NumberFormatInfo GetNumberFormatInstance (IFormatProvider fp)
594 if (_nfi != null && fp == null)
596 return NumberFormatInfo.GetInstance (fp);
599 public CultureInfo CurrentCulture {
601 if (value != null && value.IsReadOnly)
602 _nfi = value.NumberFormat;
608 private int IntegerDigits {
609 get { return _decPointPos > 0 ? _decPointPos : 1; }
612 private int DecimalDigits {
613 get { return _digitsLen > _decPointPos ? _digitsLen - _decPointPos : 0; }
616 private bool IsFloatingSource {
617 get { return _defPrecision == DoubleDefPrecision || _defPrecision == SingleDefPrecision; }
620 private bool IsZero {
621 get { return _digitsLen == 0; }
624 private bool IsZeroInteger {
625 get { return _digitsLen == 0 || _decPointPos <= 0; }
628 #endregion Helper properties
632 private void RoundPos (int pos)
634 RoundBits (_digitsLen - pos);
637 private bool RoundDecimal (int decimals)
639 return RoundBits (_digitsLen - _decPointPos - decimals);
642 private bool RoundBits (int shift)
647 if (shift > _digitsLen) {
650 _val1 = _val2 = _val3 = _val4 = 0;
655 _digitsLen += _offset;
664 shift = (shift - 1) << 2;
665 uint v = _val1 >> shift;
666 uint rem16 = v & 0xf;
667 _val1 = (v ^ rem16) << shift;
670 _val1 |= 0x99999999 >> (28 - shift);
672 int newlen = DecHexLen ();
673 res = newlen != _digitsLen;
674 _decPointPos = _decPointPos + newlen - _digitsLen;
677 RemoveTrailingZeros ();
681 private void RemoveTrailingZeros ()
683 _offset = CountTrailingZeros ();
684 _digitsLen -= _offset;
685 if (_digitsLen == 0) {
692 private void AddOneToDecHex ()
694 if (_val1 == 0x99999999) {
696 if (_val2 == 0x99999999) {
698 if (_val3 == 0x99999999) {
700 _val4 = AddOneToDecHex (_val4);
703 _val3 = AddOneToDecHex (_val3);
706 _val2 = AddOneToDecHex (_val2);
709 _val1 = AddOneToDecHex (_val1);
712 // Assume val != 0x99999999
713 private static uint AddOneToDecHex (uint val)
715 if ((val & 0xffff) == 0x9999)
716 if ((val & 0xffffff) == 0x999999)
717 if ((val & 0xfffffff) == 0x9999999)
718 return val + 0x06666667;
720 return val + 0x00666667;
721 else if ((val & 0xfffff) == 0x99999)
722 return val + 0x00066667;
724 return val + 0x00006667;
725 else if ((val & 0xff) == 0x99)
726 if ((val & 0xfff) == 0x999)
727 return val + 0x00000667;
729 return val + 0x00000067;
730 else if ((val & 0xf) == 0x9)
731 return val + 0x00000007;
736 private int CountTrailingZeros ()
739 return CountTrailingZeros (_val1);
741 return CountTrailingZeros (_val2) + 8;
743 return CountTrailingZeros (_val3) + 16;
745 return CountTrailingZeros (_val4) + 24;
749 private static int CountTrailingZeros (uint val)
751 if ((val & 0xffff) == 0)
752 if ((val & 0xffffff) == 0)
753 if ((val & 0xfffffff) == 0)
757 else if ((val & 0xfffff) == 0)
761 else if ((val & 0xff) == 0)
762 if ((val & 0xfff) == 0)
766 else if ((val & 0xf) == 0)
774 #region public number formatting methods
776 private static NumberFormatter GetInstance()
778 return Thread.CurrentThread.AcquireNumberFormatter();
781 private void Release()
783 _thread.ReleaseNumberFormatter (this);
786 public static string NumberToString (string format, sbyte value, IFormatProvider fp)
788 NumberFormatter inst = GetInstance();
789 inst.Init (format, value, Int8DefPrecision);
790 string res = inst.IntegerToString (format, fp);
795 public static string NumberToString (string format, byte value, IFormatProvider fp)
797 NumberFormatter inst = GetInstance();
798 inst.Init (format, value, UInt8DefPrecision);
799 string res = inst.IntegerToString (format, fp);
804 public static string NumberToString (string format, ushort value, IFormatProvider fp)
806 NumberFormatter inst = GetInstance();
807 inst.Init (format, value, Int16DefPrecision);
808 string res = inst.IntegerToString (format, fp);
813 public static string NumberToString (string format, short value, IFormatProvider fp)
815 NumberFormatter inst = GetInstance();
816 inst.Init (format, value, UInt16DefPrecision);
817 string res = inst.IntegerToString (format, fp);
822 public static string NumberToString (string format, uint value, IFormatProvider fp)
824 NumberFormatter inst = GetInstance();
825 inst.Init (format, value, Int32DefPrecision);
826 string res = inst.IntegerToString (format, fp);
831 public static string NumberToString (string format, int value, IFormatProvider fp)
833 NumberFormatter inst = GetInstance();
834 inst.Init (format, value, UInt32DefPrecision);
835 string res = inst.IntegerToString (format, fp);
840 public static string NumberToString (string format, ulong value, IFormatProvider fp)
842 NumberFormatter inst = GetInstance();
843 inst.Init (format, value);
844 string res = inst.IntegerToString (format, fp);
849 public static string NumberToString (string format, long value, IFormatProvider fp)
851 NumberFormatter inst = GetInstance();
852 inst.Init (format, value);
853 string res = inst.IntegerToString (format, fp);
858 public static string NumberToString (string format, float value, IFormatProvider fp)
860 NumberFormatter inst = GetInstance();
861 inst.Init (format, value, SingleDefPrecision);
862 NumberFormatInfo nfi = inst.GetNumberFormatInstance (fp);
866 else if (inst._infinity)
868 res = nfi.PositiveInfinitySymbol;
870 res = nfi.NegativeInfinitySymbol;
871 else if (inst._specifier == 'R')
872 res = inst.FormatRoundtrip (value, nfi);
874 res = inst.NumberToString (format, nfi);
879 public static string NumberToString (string format, double value, IFormatProvider fp)
881 NumberFormatter inst = GetInstance();
882 inst.Init (format, value, DoubleDefPrecision);
883 NumberFormatInfo nfi = inst.GetNumberFormatInstance (fp);
887 else if (inst._infinity)
889 res = nfi.PositiveInfinitySymbol;
891 res = nfi.NegativeInfinitySymbol;
892 else if (inst._specifier == 'R')
893 res = inst.FormatRoundtrip (value, nfi);
895 res = inst.NumberToString (format, nfi);
900 public static string NumberToString (string format, decimal value, IFormatProvider fp)
902 NumberFormatter inst = GetInstance();
903 inst.Init (format, value);
904 string res = inst.NumberToString (format, inst.GetNumberFormatInstance (fp));
909 public static string NumberToString (uint value, IFormatProvider fp)
911 if (value >= HundredMillion)
912 return NumberToString (null, value, fp);
914 NumberFormatter inst = GetInstance();
915 string res = inst.FastIntegerToString ((int)value, fp);
920 public static string NumberToString (int value, IFormatProvider fp)
922 if (value >= HundredMillion || value <= -HundredMillion)
923 return NumberToString (null, value, fp);
925 NumberFormatter inst = GetInstance();
926 string res = inst.FastIntegerToString (value, fp);
931 public static string NumberToString (ulong value, IFormatProvider fp)
933 if (value >= HundredMillion)
934 return NumberToString (null, value, fp);
936 NumberFormatter inst = GetInstance();
937 string res = inst.FastIntegerToString ((int)value, fp);
942 public static string NumberToString (long value, IFormatProvider fp)
944 if (value >= HundredMillion || value <= -HundredMillion)
945 return NumberToString (null, value, fp);
947 NumberFormatter inst = GetInstance();
948 string res = inst.FastIntegerToString ((int)value, fp);
953 public static string NumberToString (float value, IFormatProvider fp)
955 NumberFormatter inst = GetInstance();
956 inst.Init (null, value, SingleDefPrecision);
957 NumberFormatInfo nfi = inst.GetNumberFormatInstance (fp);
961 else if (inst._infinity)
963 res = nfi.PositiveInfinitySymbol;
965 res = nfi.NegativeInfinitySymbol;
967 res = inst.FormatGeneral (-1, nfi);
972 public static string NumberToString (double value, IFormatProvider fp)
974 NumberFormatter inst = GetInstance();
975 NumberFormatInfo nfi = inst.GetNumberFormatInstance (fp);
976 inst.Init (null, value, DoubleDefPrecision);
980 else if (inst._infinity)
982 res = nfi.PositiveInfinitySymbol;
984 res = nfi.NegativeInfinitySymbol;
986 res = inst.FormatGeneral (-1, nfi);
991 private string FastIntegerToString (int value, IFormatProvider fp)
994 string sign = GetNumberFormatInstance(fp).NegativeSign;
995 ResetCharBuf (8 + sign.Length);
1002 if (value >= 10000) {
1003 int v = value / 10000;
1004 FastAppendDigits (v, false);
1005 FastAppendDigits (value - v * 10000, true);
1008 FastAppendDigits (value, false);
1010 return new string (_cbuf, 0, _ind);
1013 private string IntegerToString (string format, IFormatProvider fp)
1015 NumberFormatInfo nfi = GetNumberFormatInstance (fp);
1016 switch (_specifier) {
1018 return FormatCurrency (_precision, nfi);
1020 return FormatDecimal (_precision, nfi);
1022 return FormatExponential (_precision, nfi);
1024 return FormatFixedPoint (_precision, nfi);
1026 if (_precision <= 0)
1027 return FormatDecimal (-1, nfi);
1028 return FormatGeneral (_precision, nfi);
1030 return FormatNumber (_precision, nfi);
1032 return FormatPercent (_precision, nfi);
1034 return FormatHexadecimal (_precision);
1036 if (_isCustomFormat)
1037 return FormatCustom (format, nfi);
1038 throw new FormatException ("The specified format '" + format + "' is invalid");
1042 private string NumberToString (string format, NumberFormatInfo nfi)
1044 switch (_specifier) {
1046 return FormatCurrency (_precision, nfi);
1048 return FormatExponential (_precision, nfi);
1050 return FormatFixedPoint (_precision, nfi);
1052 return FormatGeneral (_precision, nfi);
1054 return FormatNumber (_precision, nfi);
1056 return FormatPercent (_precision, nfi);
1059 if (_isCustomFormat)
1060 return FormatCustom (format, nfi);
1061 throw new FormatException ("The specified format '" + format + "' is invalid");
1065 public string FormatCurrency (int precision, NumberFormatInfo nfi)
1067 precision = (precision >= 0 ? precision : nfi.CurrencyDecimalDigits);
1068 RoundDecimal (precision);
1069 ResetCharBuf (IntegerDigits * 2 + precision * 2 + 16);
1072 switch (nfi.CurrencyPositivePattern) {
1074 Append (nfi.CurrencySymbol);
1077 Append (nfi.CurrencySymbol);
1083 switch (nfi.CurrencyNegativePattern) {
1086 Append (nfi.CurrencySymbol);
1089 Append (nfi.NegativeSign);
1090 Append (nfi.CurrencySymbol);
1093 Append (nfi.CurrencySymbol);
1094 Append (nfi.NegativeSign);
1097 Append (nfi.CurrencySymbol);
1103 Append (nfi.NegativeSign);
1106 Append (nfi.NegativeSign);
1109 Append (nfi.NegativeSign);
1110 Append (nfi.CurrencySymbol);
1114 Append (nfi.CurrencySymbol);
1118 Append (nfi.CurrencySymbol);
1120 Append (nfi.NegativeSign);
1124 Append (nfi.CurrencySymbol);
1133 AppendIntegerStringWithGroupSeparator (nfi.RawCurrencyGroupSizes, nfi.CurrencyGroupSeparator);
1135 if (precision > 0) {
1136 Append (nfi.CurrencyDecimalSeparator);
1137 AppendDecimalString (precision);
1141 switch (nfi.CurrencyPositivePattern) {
1143 Append (nfi.CurrencySymbol);
1147 Append (nfi.CurrencySymbol);
1152 switch (nfi.CurrencyNegativePattern) {
1157 Append (nfi.NegativeSign);
1160 Append (nfi.CurrencySymbol);
1164 Append (nfi.CurrencySymbol);
1167 Append (nfi.NegativeSign);
1168 Append (nfi.CurrencySymbol);
1171 Append (nfi.CurrencySymbol);
1172 Append (nfi.NegativeSign);
1176 Append (nfi.CurrencySymbol);
1180 Append (nfi.CurrencySymbol);
1181 Append (nfi.NegativeSign);
1184 Append (nfi.NegativeSign);
1187 Append (nfi.NegativeSign);
1189 Append (nfi.CurrencySymbol);
1196 Append (nfi.CurrencySymbol);
1202 return new string (_cbuf, 0, _ind);
1205 private string FormatDecimal (int precision, NumberFormatInfo nfi)
1207 if (precision < _digitsLen)
1208 precision = _digitsLen;
1212 ResetCharBuf (precision + 1);
1214 Append (nfi.NegativeSign);
1215 AppendDigits (0, precision);
1217 return new string (_cbuf, 0, _ind);
1220 #if UNSAFE_TABLES // No unsafe code under TARGET_JVM
1223 private string FormatHexadecimal (int precision)
1225 int size = Math.Max (precision, _decPointPos);
1227 char* digits = _specifierIsUpper ? DigitUpperTable : DigitLowerTable;
1229 char[] digits = _specifierIsUpper ? DigitUpperTable : DigitLowerTable;
1231 ResetCharBuf (size);
1233 ulong val = _val1 | ((ulong)_val2 << 32);
1235 _cbuf [--size] = digits [val & 0xf];
1238 return new string (_cbuf, 0, _ind);
1241 public string FormatFixedPoint (int precision, NumberFormatInfo nfi)
1243 if (precision == -1)
1244 precision = nfi.NumberDecimalDigits;
1246 RoundDecimal (precision);
1248 ResetCharBuf (IntegerDigits + precision + 2);
1251 Append (nfi.NegativeSign);
1253 AppendIntegerString (IntegerDigits);
1255 if (precision > 0) {
1256 Append (nfi.NumberDecimalSeparator);
1257 AppendDecimalString (precision);
1260 return new string (_cbuf, 0, _ind);
1263 private string FormatRoundtrip (double origval, NumberFormatInfo nfi)
1265 NumberFormatter nfc = GetClone ();
1266 if (origval >= MinRoundtripVal && origval <= MaxRoundtripVal) {
1267 string shortRep = FormatGeneral (_defPrecision, nfi);
1268 if (origval == Double.Parse (shortRep, nfi))
1271 return nfc.FormatGeneral (_defPrecision + 2, nfi);
1274 private string FormatRoundtrip (float origval, NumberFormatInfo nfi)
1276 NumberFormatter nfc = GetClone ();
1277 string shortRep = FormatGeneral (_defPrecision, nfi);
1278 // Check roundtrip only for "normal" double values.
1279 if (origval == Single.Parse (shortRep, nfi))
1281 return nfc.FormatGeneral (_defPrecision + 2, nfi);
1284 private string FormatGeneral (int precision, NumberFormatInfo nfi)
1287 if (precision == -1) {
1288 enableExp = IsFloatingSource;
1289 precision = _defPrecision;
1294 precision = _defPrecision;
1295 RoundPos (precision);
1298 int intDigits = _decPointPos;
1299 int digits = _digitsLen;
1300 int decDigits = digits - intDigits;
1302 if ((intDigits > precision || intDigits <= -4) && enableExp)
1303 return FormatExponential (digits - 1, nfi, 2);
1309 ResetCharBuf (decDigits + intDigits + 3);
1312 Append (nfi.NegativeSign);
1317 AppendDigits (digits - intDigits, digits);
1319 if (decDigits > 0) {
1320 Append (nfi.NumberDecimalSeparator);
1321 AppendDigits (0, decDigits);
1324 return new string (_cbuf, 0, _ind);
1327 public string FormatNumber (int precision, NumberFormatInfo nfi)
1329 precision = (precision >= 0 ? precision : nfi.NumberDecimalDigits);
1330 ResetCharBuf (IntegerDigits * 3 + precision);
1331 RoundDecimal (precision);
1334 switch (nfi.NumberNegativePattern) {
1339 Append (nfi.NegativeSign);
1342 Append (nfi.NegativeSign);
1348 AppendIntegerStringWithGroupSeparator (nfi.RawNumberGroupSizes, nfi.NumberGroupSeparator);
1350 if (precision > 0) {
1351 Append (nfi.NumberDecimalSeparator);
1352 AppendDecimalString (precision);
1356 switch (nfi.NumberNegativePattern) {
1361 Append (nfi.NegativeSign);
1365 Append (nfi.NegativeSign);
1370 return new string (_cbuf, 0, _ind);
1373 public string FormatPercent (int precision, NumberFormatInfo nfi)
1375 precision = (precision >= 0 ? precision : nfi.PercentDecimalDigits);
1377 RoundDecimal (precision);
1378 ResetCharBuf (IntegerDigits * 2 + precision + 16);
1381 if (nfi.PercentPositivePattern == 2)
1382 Append (nfi.PercentSymbol);
1385 switch (nfi.PercentNegativePattern) {
1387 Append (nfi.NegativeSign);
1390 Append (nfi.NegativeSign);
1393 Append (nfi.NegativeSign);
1394 Append (nfi.PercentSymbol);
1399 AppendIntegerStringWithGroupSeparator (nfi.RawPercentGroupSizes, nfi.PercentGroupSeparator);
1401 if (precision > 0) {
1402 Append (nfi.PercentDecimalSeparator);
1403 AppendDecimalString (precision);
1407 switch (nfi.PercentPositivePattern) {
1410 Append (nfi.PercentSymbol);
1413 Append (nfi.PercentSymbol);
1418 switch (nfi.PercentNegativePattern) {
1421 Append (nfi.PercentSymbol);
1424 Append (nfi.PercentSymbol);
1429 return new string (_cbuf, 0, _ind);
1432 public string FormatExponential (int precision, NumberFormatInfo nfi)
1434 if (precision == -1)
1435 precision = DefaultExpPrecision;
1437 RoundPos (precision + 1);
1438 return FormatExponential (precision, nfi, 3);
1441 private string FormatExponential (int precision, NumberFormatInfo nfi, int expDigits)
1443 int decDigits = _decPointPos;
1444 int digits = _digitsLen;
1445 int exponent = decDigits - 1;
1446 decDigits = _decPointPos = 1;
1448 ResetCharBuf (precision + 8);
1451 Append (nfi.NegativeSign);
1453 AppendOneDigit (digits - 1);
1455 if (precision > 0) {
1456 Append (nfi.NumberDecimalSeparator);
1457 AppendDigits (digits - precision - 1, digits - _decPointPos);
1460 AppendExponent (nfi, exponent, expDigits);
1462 return new string (_cbuf, 0, _ind);
1465 public string FormatCustom (string format, NumberFormatInfo nfi)
1470 CustomInfo.GetActiveSection (format, ref p, IsZero, ref offset, ref length);
1472 return _positive ? string.Empty : nfi.NegativeSign;
1475 CustomInfo info = CustomInfo.Parse (format, offset, length, nfi);
1477 Console.WriteLine ("Format : {0}",format);
1478 Console.WriteLine ("DecimalDigits : {0}",info.DecimalDigits);
1479 Console.WriteLine ("DecimalPointPos : {0}",info.DecimalPointPos);
1480 Console.WriteLine ("DecimalTailSharpDigits : {0}",info.DecimalTailSharpDigits);
1481 Console.WriteLine ("IntegerDigits : {0}",info.IntegerDigits);
1482 Console.WriteLine ("IntegerHeadSharpDigits : {0}",info.IntegerHeadSharpDigits);
1483 Console.WriteLine ("IntegerHeadPos : {0}",info.IntegerHeadPos);
1484 Console.WriteLine ("UseExponent : {0}",info.UseExponent);
1485 Console.WriteLine ("ExponentDigits : {0}",info.ExponentDigits);
1486 Console.WriteLine ("ExponentTailSharpDigits : {0}",info.ExponentTailSharpDigits);
1487 Console.WriteLine ("ExponentNegativeSignOnly : {0}",info.ExponentNegativeSignOnly);
1488 Console.WriteLine ("DividePlaces : {0}",info.DividePlaces);
1489 Console.WriteLine ("Percents : {0}",info.Percents);
1490 Console.WriteLine ("Permilles : {0}",info.Permilles);
1492 StringBuilder sb_int = new StringBuilder (info.IntegerDigits * 2);
1493 StringBuilder sb_dec = new StringBuilder (info.DecimalDigits * 2);
1494 StringBuilder sb_exp = (info.UseExponent ? new StringBuilder (info.ExponentDigits * 2) : null);
1497 if (info.Percents > 0)
1498 Multiply10(2 * info.Percents);
1499 if (info.Permilles > 0)
1500 Multiply10(3 * info.Permilles);
1501 if (info.DividePlaces > 0)
1502 Divide10(info.DividePlaces);
1504 bool expPositive = true;
1505 if (info.UseExponent && (info.DecimalDigits > 0 || info.IntegerDigits > 0)) {
1507 RoundPos (info.DecimalDigits + info.IntegerDigits);
1508 diff -= _decPointPos - info.IntegerDigits;
1509 _decPointPos = info.IntegerDigits;
1512 expPositive = diff <= 0;
1513 AppendNonNegativeNumber (sb_exp, diff < 0 ? -diff : diff);
1516 RoundDecimal (info.DecimalDigits);
1518 if (info.IntegerDigits != 0 || !IsZeroInteger)
1519 AppendIntegerString (IntegerDigits, sb_int);
1521 AppendDecimalString (DecimalDigits, sb_dec);
1523 if (info.UseExponent) {
1524 if (info.DecimalDigits <= 0 && info.IntegerDigits <= 0)
1527 if (sb_int.Length < info.IntegerDigits)
1528 sb_int.Insert (0, "0", info.IntegerDigits - sb_int.Length);
1530 while (sb_exp.Length < info.ExponentDigits - info.ExponentTailSharpDigits)
1531 sb_exp.Insert (0, '0');
1533 if (expPositive && !info.ExponentNegativeSignOnly)
1534 sb_exp.Insert (0, nfi.PositiveSign);
1535 else if (!expPositive)
1536 sb_exp.Insert (0, nfi.NegativeSign);
1539 if (sb_int.Length < info.IntegerDigits - info.IntegerHeadSharpDigits)
1540 sb_int.Insert (0, "0", info.IntegerDigits - info.IntegerHeadSharpDigits - sb_int.Length);
1541 if (info.IntegerDigits == info.IntegerHeadSharpDigits && IsZeroOnly (sb_int))
1542 sb_int.Remove (0, sb_int.Length);
1545 ZeroTrimEnd (sb_dec, true);
1546 while (sb_dec.Length < info.DecimalDigits - info.DecimalTailSharpDigits)
1547 sb_dec.Append ('0');
1548 if (sb_dec.Length > info.DecimalDigits)
1549 sb_dec.Remove (info.DecimalDigits, sb_dec.Length - info.DecimalDigits);
1551 return info.Format (format, offset, length, nfi, _positive, sb_int, sb_dec, sb_exp);
1553 #endregion public number formatting methods
1555 #region StringBuilder formatting helpers
1557 private static void ZeroTrimEnd (StringBuilder sb)
1559 ZeroTrimEnd (sb, false);
1562 private static void ZeroTrimEnd (StringBuilder sb, bool canEmpty)
1565 for (int i = sb.Length - 1; (canEmpty ? i >= 0 : i > 0); i--) {
1572 sb.Remove (sb.Length - len, len);
1575 private static bool IsZeroOnly (StringBuilder sb)
1577 for (int i = 0; i < sb.Length; i++)
1578 if (char.IsDigit (sb [i]) && sb [i] != '0')
1583 private static void AppendNonNegativeNumber (StringBuilder sb, int v)
1586 throw new ArgumentException ();
1588 int i = ScaleOrder (v) - 1;
1590 int n = v / (int)GetTenPowerOf (i);
1591 sb.Append ((char)('0' | n));
1592 v -= (int)GetTenPowerOf (i--) * n;
1596 #endregion StringBuilder formatting helpers
1598 #region Append helpers
1600 private void AppendIntegerString (int minLength, StringBuilder sb)
1602 if (_decPointPos <= 0) {
1603 sb.Append ('0', minLength);
1607 if (_decPointPos < minLength)
1608 sb.Append ('0', minLength - _decPointPos);
1610 AppendDigits (_digitsLen - _decPointPos, _digitsLen, sb);
1613 private void AppendIntegerString (int minLength)
1615 if (_decPointPos <= 0) {
1616 Append ('0', minLength);
1620 if (_decPointPos < minLength)
1621 Append ('0', minLength - _decPointPos);
1623 AppendDigits (_digitsLen - _decPointPos, _digitsLen);
1626 private void AppendDecimalString (int precision, StringBuilder sb)
1628 AppendDigits (_digitsLen - precision - _decPointPos, _digitsLen - _decPointPos, sb);
1631 private void AppendDecimalString (int precision)
1633 AppendDigits (_digitsLen - precision - _decPointPos, _digitsLen - _decPointPos);
1636 private void AppendIntegerStringWithGroupSeparator (int[] groups, string groupSeparator)
1638 if (IsZeroInteger) {
1645 for (int i = 0; i < groups.Length; i++) {
1646 total += groups [i];
1647 if (total <= _decPointPos)
1653 if (groups.Length > 0 && total > 0) {
1655 int groupSize = groups [groupIndex];
1656 int fraction = _decPointPos > total ? _decPointPos - total : 0;
1657 if (groupSize == 0) {
1658 while (groupIndex >= 0 && groups [groupIndex] == 0)
1661 groupSize = fraction > 0 ? fraction : groups [groupIndex];
1664 counter = groupSize;
1666 groupIndex += fraction / groupSize;
1667 counter = fraction % groupSize;
1669 counter = groupSize;
1674 for (int i = 0; ;) {
1675 if ((_decPointPos - i) <= counter || counter == 0) {
1676 AppendDigits (_digitsLen - _decPointPos, _digitsLen - i);
1679 AppendDigits (_digitsLen - i - counter, _digitsLen - i);
1681 Append (groupSeparator);
1682 if (--groupIndex < groups.Length && groupIndex >= 0)
1683 groupSize = groups [groupIndex];
1684 counter = groupSize;
1688 AppendDigits (_digitsLen - _decPointPos, _digitsLen);
1692 // minDigits is in the range 1..3
1693 private void AppendExponent (NumberFormatInfo nfi, int exponent, int minDigits)
1695 if (_specifierIsUpper || _specifier == 'R')
1701 Append (nfi.PositiveSign);
1703 Append (nfi.NegativeSign);
1704 exponent = -exponent;
1708 Append ('0', minDigits);
1709 else if (exponent < 10) {
1710 Append ('0', minDigits - 1);
1711 Append ((char)('0' | exponent));
1714 uint hexDigit = FastToDecHex (exponent);
1715 if (exponent >= 100 || minDigits == 3)
1716 Append ((char)('0' | (hexDigit >> 8)));
1717 Append ((char)('0' | ((hexDigit >> 4) & 0xf)));
1718 Append ((char)('0' | (hexDigit & 0xf)));
1722 private void AppendOneDigit (int start)
1724 if (_ind == _cbuf.Length)
1733 else if (start < 16)
1735 else if (start < 24)
1737 else if (start < 32)
1741 v >>= (start & 0x7) << 2;
1742 _cbuf [_ind++] = (char)('0' | v & 0xf);
1745 #if UNSAFE_TABLES // No unsafe code under TARGET_JVM
1748 private void FastAppendDigits (int val, bool force)
1752 if (force || val >= 100) {
1753 int v = (val * 5243) >> 19;
1754 digits = DecHexDigits [v];
1755 if (force || val >= 1000)
1756 _cbuf [i++] = (char)('0' | digits >> 4);
1757 _cbuf [i++] = (char)('0' | (digits & 0xf));
1758 digits = DecHexDigits [val - v * 100];
1761 digits = DecHexDigits [val];
1763 if (force || val >= 10)
1764 _cbuf [i++] = (char)('0' | digits >> 4);
1765 _cbuf [i++] = (char)('0' | (digits & 0xf));
1769 private void AppendDigits (int start, int end)
1774 int i = _ind + (end - start);
1775 if (i > _cbuf.Length)
1782 for (int next = start + 8 - (start & 0x7); ; start = next, next += 8) {
1786 else if (next == 16)
1788 else if (next == 24)
1790 else if (next == 32)
1794 v >>= (start & 0x7) << 2;
1798 _cbuf [--i] = (char)('0' | v & 0xf);
1799 switch (next - start) {
1801 _cbuf [--i] = (char)('0' | (v >>= 4) & 0xf);
1804 _cbuf [--i] = (char)('0' | (v >>= 4) & 0xf);
1807 _cbuf [--i] = (char)('0' | (v >>= 4) & 0xf);
1810 _cbuf [--i] = (char)('0' | (v >>= 4) & 0xf);
1813 _cbuf [--i] = (char)('0' | (v >>= 4) & 0xf);
1816 _cbuf [--i] = (char)('0' | (v >>= 4) & 0xf);
1819 _cbuf [--i] = (char)('0' | (v >>= 4) & 0xf);
1829 private void AppendDigits (int start, int end, StringBuilder sb)
1834 int i = sb.Length + (end - start);
1840 for (int next = start + 8 - (start & 0x7); ; start = next, next += 8) {
1844 else if (next == 16)
1846 else if (next == 24)
1848 else if (next == 32)
1852 v >>= (start & 0x7) << 2;
1855 sb [--i] = (char)('0' | v & 0xf);
1856 switch (next - start) {
1858 sb [--i] = (char)('0' | (v >>= 4) & 0xf);
1861 sb [--i] = (char)('0' | (v >>= 4) & 0xf);
1864 sb [--i] = (char)('0' | (v >>= 4) & 0xf);
1867 sb [--i] = (char)('0' | (v >>= 4) & 0xf);
1870 sb [--i] = (char)('0' | (v >>= 4) & 0xf);
1873 sb [--i] = (char)('0' | (v >>= 4) & 0xf);
1876 sb [--i] = (char)('0' | (v >>= 4) & 0xf);
1886 #endregion Append helpers
1890 private void Multiply10(int count)
1892 if (count <= 0 || _digitsLen == 0)
1895 _decPointPos += count;
1898 private void Divide10(int count)
1900 if (count <= 0 || _digitsLen == 0)
1903 _decPointPos -= count;
1906 private NumberFormatter GetClone ()
1908 return (NumberFormatter)this.MemberwiseClone ();
1915 private class CustomInfo
1917 public bool UseGroup = false;
1918 public int DecimalDigits = 0;
1919 public int DecimalPointPos = -1;
1920 public int DecimalTailSharpDigits = 0;
1921 public int IntegerDigits = 0;
1922 public int IntegerHeadSharpDigits = 0;
1923 public int IntegerHeadPos = 0;
1924 public bool UseExponent = false;
1925 public int ExponentDigits = 0;
1926 public int ExponentTailSharpDigits = 0;
1927 public bool ExponentNegativeSignOnly = true;
1928 public int DividePlaces = 0;
1929 public int Percents = 0;
1930 public int Permilles = 0;
1932 public static void GetActiveSection (string format, ref bool positive, bool zero, ref int offset, ref int length)
1934 int[] lens = new int [3];
1937 char literal = '\0';
1938 for (int i = 0; i < format.Length; i++) {
1939 char c = format [i];
1941 if (c == literal || (literal == '\0' && (c == '\"' || c == '\''))) {
1942 if (literal == '\0')
1949 if (literal == '\0' && format [i] == ';' && (i == 0 || format [i - 1] != '\\')) {
1950 lens [index++] = i - lastPos;
1959 length = format.Length;
1963 if (positive || zero) {
1968 if (lens [0] + 1 < format.Length) {
1970 offset = lens [0] + 1;
1971 length = format.Length - offset;
1982 offset = lens [0] + lens [1] + 2;
1983 length = format.Length - offset;
1993 offset = lens [0] + 1;
2005 offset = lens [0] + lens [1] + 2;
2016 offset = lens [0] + 1;
2027 throw new ArgumentException ();
2030 public static CustomInfo Parse (string format, int offset, int length, NumberFormatInfo nfi)
2032 char literal = '\0';
2033 bool integerArea = true;
2034 bool decimalArea = false;
2035 bool exponentArea = false;
2036 bool sharpContinues = true;
2038 CustomInfo info = new CustomInfo ();
2039 int groupSeparatorCounter = 0;
2041 for (int i = offset; i - offset < length; i++) {
2042 char c = format [i];
2044 if (c == literal && c != '\0') {
2048 if (literal != '\0')
2051 if (exponentArea && (c != '\0' && c != '0' && c != '#')) {
2052 exponentArea = false;
2053 integerArea = (info.DecimalPointPos < 0);
2054 decimalArea = !integerArea;
2065 if (c == '\"' || c == '\'') {
2070 if (sharpContinues && integerArea)
2071 info.IntegerHeadSharpDigits++;
2072 else if (decimalArea)
2073 info.DecimalTailSharpDigits++;
2074 else if (exponentArea)
2075 info.ExponentTailSharpDigits++;
2080 sharpContinues = false;
2082 info.DecimalTailSharpDigits = 0;
2083 else if (exponentArea)
2084 info.ExponentTailSharpDigits = 0;
2086 if (info.IntegerHeadPos == -1)
2087 info.IntegerHeadPos = i;
2090 info.IntegerDigits++;
2091 if (groupSeparatorCounter > 0)
2092 info.UseGroup = true;
2093 groupSeparatorCounter = 0;
2095 else if (decimalArea)
2096 info.DecimalDigits++;
2097 else if (exponentArea)
2098 info.ExponentDigits++;
2102 if (info.UseExponent)
2105 info.UseExponent = true;
2106 integerArea = false;
2107 decimalArea = false;
2108 exponentArea = true;
2109 if (i + 1 - offset < length) {
2110 char nc = format [i + 1];
2112 info.ExponentNegativeSignOnly = false;
2113 if (nc == '+' || nc == '-')
2115 else if (nc != '0' && nc != '#') {
2116 info.UseExponent = false;
2117 if (info.DecimalPointPos < 0)
2124 integerArea = false;
2126 exponentArea = false;
2127 if (info.DecimalPointPos == -1)
2128 info.DecimalPointPos = i;
2137 if (integerArea && info.IntegerDigits > 0)
2138 groupSeparatorCounter++;
2145 if (info.ExponentDigits == 0)
2146 info.UseExponent = false;
2148 info.IntegerHeadSharpDigits = 0;
2150 if (info.DecimalDigits == 0)
2151 info.DecimalPointPos = -1;
2153 info.DividePlaces += groupSeparatorCounter * 3;
2158 public string Format (string format, int offset, int length, NumberFormatInfo nfi, bool positive, StringBuilder sb_int, StringBuilder sb_dec, StringBuilder sb_exp)
2160 StringBuilder sb = new StringBuilder ();
2161 char literal = '\0';
2162 bool integerArea = true;
2163 bool decimalArea = false;
2164 int intSharpCounter = 0;
2165 int sb_int_index = 0;
2166 int sb_dec_index = 0;
2168 int[] groups = nfi.RawNumberGroupSizes;
2169 string groupSeparator = nfi.NumberGroupSeparator;
2170 int intLen = 0, total = 0, groupIndex = 0, counter = 0, groupSize = 0;
2171 if (UseGroup && groups.Length > 0) {
2172 intLen = sb_int.Length;
2173 for (int i = 0; i < groups.Length; i++) {
2174 total += groups [i];
2175 if (total <= intLen)
2178 groupSize = groups [groupIndex];
2179 int fraction = intLen > total ? intLen - total : 0;
2180 if (groupSize == 0) {
2181 while (groupIndex >= 0 && groups [groupIndex] == 0)
2184 groupSize = fraction > 0 ? fraction : groups [groupIndex];
2187 counter = groupSize;
2189 groupIndex += fraction / groupSize;
2190 counter = fraction % groupSize;
2192 counter = groupSize;
2200 for (int i = offset; i - offset < length; i++) {
2201 char c = format [i];
2203 if (c == literal && c != '\0') {
2207 if (literal != '\0') {
2215 if (i - offset < length)
2216 sb.Append (format [i]);
2220 if (c == '\"' || c == '\'')
2228 if (IntegerDigits - intSharpCounter < sb_int.Length + sb_int_index || c == '0')
2229 while (IntegerDigits - intSharpCounter + sb_int_index < sb_int.Length) {
2230 sb.Append (sb_int [sb_int_index++]);
2231 if (UseGroup && --intLen > 0 && --counter == 0) {
2232 sb.Append (groupSeparator);
2233 if (--groupIndex < groups.Length && groupIndex >= 0)
2234 groupSize = groups [groupIndex];
2235 counter = groupSize;
2240 else if (decimalArea) {
2241 if (sb_dec_index < sb_dec.Length)
2242 sb.Append (sb_dec [sb_dec_index++]);
2250 if (sb_exp == null || !UseExponent) {
2259 for (q = i + 1; q - offset < length; q++) {
2260 if (format [q] == '0') {
2264 if (q == i + 1 && (format [q] == '+' || format [q] == '-'))
2273 integerArea = (DecimalPointPos < 0);
2274 decimalArea = !integerArea;
2285 if (DecimalPointPos == i) {
2286 if (DecimalDigits > 0) {
2287 while (sb_int_index < sb_int.Length)
2288 sb.Append (sb_int [sb_int_index++]);
2290 if (sb_dec.Length > 0)
2291 sb.Append (nfi.NumberDecimalSeparator);
2293 integerArea = false;
2299 sb.Append (nfi.PercentSymbol);
2302 sb.Append (nfi.PerMilleSymbol);
2311 sb.Insert (0, nfi.NegativeSign);
2313 return sb.ToString ();