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 using System.Globalization;
33 using System.Threading;
34 using System.Runtime.CompilerServices;
38 internal sealed partial class NumberFormatter
42 const int DefaultExpPrecision = 6;
43 const int HundredMillion = 100000000;
44 const long SeventeenDigitsThreshold = 10000000000000000;
45 const ulong ULongDivHundredMillion = UInt64.MaxValue / HundredMillion;
46 const ulong ULongModHundredMillion = 1 + UInt64.MaxValue % HundredMillion;
48 const int DoubleBitsExponentShift = 52;
49 const int DoubleBitsExponentMask = 0x7ff;
50 const long DoubleBitsMantissaMask = 0xfffffffffffff;
51 const int DecimalBitsScaleMask = 0x1f0000;
53 const int SingleDefPrecision = 7;
54 const int DoubleDefPrecision = 15;
55 const int Int8DefPrecision = 3;
56 const int UInt8DefPrecision = 3;
57 const int Int16DefPrecision = 5;
58 const int UInt16DefPrecision = 5;
59 const int Int32DefPrecision = 10;
60 const int UInt32DefPrecision = 10;
61 const int Int64DefPrecision = 19;
62 const int UInt64DefPrecision = 20;
63 const int DecimalDefPrecision = 100;
64 const int TenPowersListLength = 19;
66 const double MinRoundtripVal = -1.79769313486231E+308;
67 const double MaxRoundtripVal = 1.79769313486231E+308;
69 // The below arrays are taken from mono/metatdata/number-formatter.h
71 private static readonly unsafe ulong* MantissaBitsTable;
72 private static readonly unsafe int* TensExponentTable;
73 private static readonly unsafe char* DigitLowerTable;
74 private static readonly unsafe char* DigitUpperTable;
75 private static readonly unsafe long* TenPowersList;
77 // DecHexDigits s a translation table from a decimal number to its
78 // digits hexadecimal representation (e.g. DecHexDigits [34] = 0x34).
79 private static readonly unsafe int* DecHexDigits;
81 [MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.InternalCall)]
82 private unsafe static extern void GetFormatterTables (out ulong* MantissaBitsTable, out int* TensExponentTable,
83 out char* DigitLowerTable, out char* DigitUpperTable,
84 out long* TenPowersList, out int* DecHexDigits);
86 unsafe static NumberFormatter()
88 GetFormatterTables (out MantissaBitsTable, out TensExponentTable,
89 out DigitLowerTable, out DigitUpperTable, out TenPowersList, out DecHexDigits);
92 unsafe static long GetTenPowerOf(int i)
94 return TenPowersList [i];
96 #endregion Static Fields
100 private NumberFormatInfo _nfi;
102 //part of the private stringbuffer
103 private char[] _cbuf;
106 private bool _infinity;
107 private bool _isCustomFormat;
108 private bool _specifierIsUpper;
109 private bool _positive;
110 private char _specifier;
111 private int _precision;
112 private int _defPrecision;
114 private int _digitsLen;
115 private int _offset; // Represent the first digit offset.
116 private int _decPointPos;
118 // The following fields are a hexadeimal representation of the digits.
119 // For instance _val = 0x234 represents the digits '2', '3', '4'.
120 private uint _val1; // Digits 0 - 7.
121 private uint _val2; // Digits 8 - 15.
122 private uint _val3; // Digits 16 - 23.
123 private uint _val4; // Digits 23 - 31. Only needed for decimals.
127 #region Constructor Helpers
129 // Translate an unsigned int to hexadecimal digits.
130 // i.e. 123456789 is represented by _val1 = 0x23456789 and _val2 = 0x1
131 private void InitDecHexDigits (uint value)
133 if (value >= HundredMillion) {
134 int div1 = (int)(value / HundredMillion);
135 value -= HundredMillion * (uint)div1;
136 _val2 = FastToDecHex (div1);
138 _val1 = ToDecHex ((int)value);
141 // Translate an unsigned long to hexadecimal digits.
142 private void InitDecHexDigits (ulong value)
144 if (value >= HundredMillion) {
145 long div1 = (long)(value / HundredMillion);
146 value -= HundredMillion * (ulong)div1;
147 if (div1 >= HundredMillion) {
148 int div2 = (int)(div1 / HundredMillion);
149 div1 = div1 - div2 * (long)HundredMillion;
150 _val3 = ToDecHex (div2);
153 _val2 = ToDecHex ((int)(div1));
156 _val1 = ToDecHex ((int)value);
159 // Translate a decimal integer to hexadecimal digits.
160 // The decimal integer is 96 digits and its value is hi * 2^64 + lo.
161 // is the lower 64 bits.
162 private void InitDecHexDigits (uint hi, ulong lo)
165 InitDecHexDigits (lo); // Only the lower 64 bits matter.
169 // Compute (hi, lo) = (hi , lo) / HundredMillion.
170 uint divhi = hi / HundredMillion;
171 ulong remhi = hi - divhi * HundredMillion;
172 ulong divlo = lo / HundredMillion;
173 ulong remlo = lo - divlo * HundredMillion + remhi * ULongModHundredMillion;
175 lo = divlo + remhi * ULongDivHundredMillion;
176 divlo = remlo / HundredMillion;
177 remlo -= divlo * HundredMillion;
179 _val1 = ToDecHex ((int)remlo);
181 // Divide hi * 2 ^ 64 + lo by HundredMillion using the fact that
182 // hi < HundredMillion.
183 divlo = lo / HundredMillion;
184 remlo = lo - divlo * HundredMillion;
187 lo += hi * ULongDivHundredMillion;
188 remlo += hi * ULongModHundredMillion;
189 divlo = remlo / HundredMillion;
191 remlo -= divlo * HundredMillion;
193 _val2 = ToDecHex ((int)remlo);
195 // Now we are left with 64 bits store in lo.
196 if (lo >= HundredMillion) {
197 divlo = lo / HundredMillion;
198 lo -= divlo * HundredMillion;
199 _val4 = ToDecHex ((int)divlo);
201 _val3 = ToDecHex ((int)lo);
204 // Helper to translate an int in the range 0 .. 9999 to its
205 // Hexadecimal digits representation.
206 unsafe private static uint FastToDecHex (int val)
209 return (uint)DecHexDigits [val];
211 // Uses 2^19 (524288) to compute val / 100 for val < 10000.
212 int v = (val * 5243) >> 19;
213 return (uint)((DecHexDigits [v] << 8) | DecHexDigits [val - v * 100]);
216 // Helper to translate an int in the range 0 .. 99999999 to its
217 // Hexadecimal digits representation.
218 private static uint ToDecHex (int val)
224 res = FastToDecHex (v) << 16;
226 return res | FastToDecHex (val);
229 // Helper to count number of hexadecimal digits in a number.
230 private static int FastDecHexLen (int val)
237 else if (val < 0x1000)
243 private static int DecHexLen (uint val)
246 return FastDecHexLen ((int)val);
247 return 4 + FastDecHexLen ((int)(val >> 16));
250 // Count number of hexadecimal digits stored in _val1 .. _val4.
251 private int DecHexLen ()
254 return DecHexLen (_val4) + 24;
256 return DecHexLen (_val3) + 16;
258 return DecHexLen (_val2) + 8;
260 return DecHexLen (_val1);
265 // Helper to count the 10th scale (number of digits) in a number
266 private static int ScaleOrder (long hi)
268 for (int i = TenPowersListLength - 1; i >= 0; i--)
269 if (hi >= GetTenPowerOf (i))
274 // Compute the initial precision for rounding a floating number
275 // according to the used format.
276 int InitialFloatingPrecision ()
278 if (_specifier == 'R')
279 return _defPrecision + 2;
280 if (_precision < _defPrecision)
281 return _defPrecision;
282 if (_specifier == 'G')
283 return Math.Min (_defPrecision + 2, _precision);
284 if (_specifier == 'E')
285 return Math.Min (_defPrecision + 2, _precision + 1);
286 return _defPrecision;
289 // Parse the given format and extract the precision in it.
290 // Returns -1 for empty formats and -2 to indicate that the format
291 // is a custom format.
292 private static int ParsePrecision (string format)
295 for (int i = 1; i < format.Length; i++) {
296 int val = format [i] - '0';
297 precision = precision * 10 + val;
298 if (val < 0 || val > 9 || precision > 99)
304 #endregion Constructor Helpers
308 // Parse the given format and initialize the following fields:
309 // _isCustomFormat, _specifierIsUpper, _specifier & _precision.
310 public NumberFormatter (Thread current)
312 _cbuf = EmptyArray<char>.Value;
315 CurrentCulture = current.CurrentCulture;
318 private void Init (string format)
320 _val1 = _val2 = _val3 = _val4 = 0;
322 _NaN = _infinity = false;
323 _isCustomFormat = false;
324 _specifierIsUpper = true;
327 if (format == null || format.Length == 0) {
332 char specifier = format [0];
333 if (specifier >= 'a' && specifier <= 'z') {
334 specifier = (char)(specifier - 'a' + 'A');
335 _specifierIsUpper = false;
337 else if (specifier < 'A' || specifier > 'Z') {
338 _isCustomFormat = true;
342 _specifier = specifier;
343 if (format.Length > 1) {
344 _precision = ParsePrecision (format);
345 if (_precision == -2) { // Is it a custom format?
346 _isCustomFormat = true;
353 private void InitHex (ulong value)
355 switch (_defPrecision) {
356 case Int8DefPrecision: value = (byte) value; break;
357 case Int16DefPrecision: value = (ushort) value; break;
358 case Int32DefPrecision: value = (uint) value; break;
361 _val2 = (uint)(value >> 32);
362 _decPointPos = _digitsLen = DecHexLen ();
367 private void Init (string format, int value, int defPrecision)
370 _defPrecision = defPrecision;
371 _positive = value >= 0;
373 if (value == 0 || _specifier == 'X') {
374 InitHex ((ulong)value);
380 InitDecHexDigits ((uint)value);
381 _decPointPos = _digitsLen = DecHexLen ();
384 private void Init (string format, uint value, int defPrecision)
387 _defPrecision = defPrecision;
390 if (value == 0 || _specifier == 'X') {
395 InitDecHexDigits (value);
396 _decPointPos = _digitsLen = DecHexLen ();
399 private void Init (string format, long value)
402 _defPrecision = Int64DefPrecision;
403 _positive = value >= 0;
405 if (value == 0 || _specifier == 'X') {
406 InitHex ((ulong)value);
412 InitDecHexDigits ((ulong)value);
413 _decPointPos = _digitsLen = DecHexLen ();
416 private void Init (string format, ulong value)
419 _defPrecision = UInt64DefPrecision;
422 if (value == 0 || _specifier == 'X') {
423 InitHex ((ulong)value);
427 InitDecHexDigits (value);
428 _decPointPos = _digitsLen = DecHexLen ();
431 unsafe private void Init (string format, double value, int defPrecision)
435 _defPrecision = defPrecision;
436 long bits = BitConverter.DoubleToInt64Bits (value);
437 _positive = bits >= 0;
438 bits &= Int64.MaxValue;
446 int e = (int)(bits >> DoubleBitsExponentShift);
447 long m = bits & DoubleBitsMantissaMask;
448 if (e == DoubleBitsExponentMask) {
456 // We need 'm' to be large enough so we won't lose precision.
458 int scale = ScaleOrder (m);
459 if (scale < DoubleDefPrecision) {
460 expAdjust = scale - DoubleDefPrecision;
461 m *= GetTenPowerOf (-expAdjust);
465 m = (m + DoubleBitsMantissaMask + 1) * 10;
469 // multiply the mantissa by 10 ^ N
471 ulong hi = (ulong)m >> 32;
472 ulong lo2 = MantissaBitsTable [e];
473 ulong hi2 = lo2 >> 32;
475 ulong mm = hi * lo2 + lo * hi2 + ((lo * lo2) >> 32);
476 long res = (long)(hi * hi2 + (mm >> 32));
477 while (res < SeventeenDigitsThreshold) {
478 mm = (mm & UInt32.MaxValue) * 10;
479 res = res * 10 + (long)(mm >> 32);
482 if ((mm & 0x80000000) != 0)
485 int order = DoubleDefPrecision + 2;
486 _decPointPos = TensExponentTable [e] + expAdjust + order;
488 // Rescale 'res' to the initial precision (15-17 for doubles).
489 int initialPrecision = InitialFloatingPrecision ();
490 if (order > initialPrecision) {
491 long val = GetTenPowerOf (order - initialPrecision);
492 res = (res + (val >> 1)) / val;
493 order = initialPrecision;
495 if (res >= GetTenPowerOf (order)) {
500 InitDecHexDigits ((ulong)res);
501 _offset = CountTrailingZeros ();
502 _digitsLen = order - _offset;
505 private void Init (string format, decimal value)
508 _defPrecision = DecimalDefPrecision;
510 int[] bits = decimal.GetBits (value);
511 int scale = (bits [3] & DecimalBitsScaleMask) >> 16;
512 _positive = bits [3] >= 0;
513 if (bits [0] == 0 && bits [1] == 0 && bits [2] == 0) {
514 _decPointPos = -scale;
520 InitDecHexDigits ((uint)bits [2], ((ulong)bits [1] << 32) | (uint)bits [0]);
521 _digitsLen = DecHexLen ();
522 _decPointPos = _digitsLen - scale;
523 if (_precision != -1 || _specifier != 'G') {
524 _offset = CountTrailingZeros ();
525 _digitsLen -= _offset;
529 #endregion Constructors
531 #region Inner String Buffer
533 //_cbuf moved to before other fields to improve layout
536 private void ResetCharBuf (int size)
539 if (_cbuf.Length < size)
540 _cbuf = new char [size];
543 private void Resize (int len)
545 Array.Resize (ref _cbuf, len);
548 private void Append (char c)
550 if (_ind == _cbuf.Length)
555 private void Append (char c, int cnt)
557 if (_ind + cnt > _cbuf.Length)
558 Resize (_ind + cnt + 10);
563 private void Append (string s)
566 if (_ind + slen > _cbuf.Length)
567 Resize (_ind + slen + 10);
568 for (int i = 0; i < slen; i++)
569 _cbuf [_ind++] = s [i];
572 #endregion Inner String Buffer
574 #region Helper properties
576 private NumberFormatInfo GetNumberFormatInstance (IFormatProvider fp)
578 if (_nfi != null && fp == null)
580 return NumberFormatInfo.GetInstance (fp);
583 public CultureInfo CurrentCulture {
585 if (value != null && value.IsReadOnly)
586 _nfi = value.NumberFormat;
592 private int IntegerDigits {
593 get { return _decPointPos > 0 ? _decPointPos : 1; }
596 private int DecimalDigits {
597 get { return _digitsLen > _decPointPos ? _digitsLen - _decPointPos : 0; }
600 private bool IsFloatingSource {
601 get { return _defPrecision == DoubleDefPrecision || _defPrecision == SingleDefPrecision; }
604 private bool IsZero {
605 get { return _digitsLen == 0; }
608 private bool IsZeroInteger {
609 get { return _digitsLen == 0 || _decPointPos <= 0; }
612 #endregion Helper properties
616 private void RoundPos (int pos)
618 RoundBits (_digitsLen - pos);
621 private bool RoundDecimal (int decimals)
623 return RoundBits (_digitsLen - _decPointPos - decimals);
626 private bool RoundBits (int shift)
631 if (shift > _digitsLen) {
634 _val1 = _val2 = _val3 = _val4 = 0;
639 _digitsLen += _offset;
648 shift = (shift - 1) << 2;
649 uint v = _val1 >> shift;
650 uint rem16 = v & 0xf;
651 _val1 = (v ^ rem16) << shift;
654 _val1 |= 0x99999999 >> (28 - shift);
656 int newlen = DecHexLen ();
657 res = newlen != _digitsLen;
658 _decPointPos = _decPointPos + newlen - _digitsLen;
661 RemoveTrailingZeros ();
665 private void RemoveTrailingZeros ()
667 _offset = CountTrailingZeros ();
668 _digitsLen -= _offset;
669 if (_digitsLen == 0) {
676 private void AddOneToDecHex ()
678 if (_val1 == 0x99999999) {
680 if (_val2 == 0x99999999) {
682 if (_val3 == 0x99999999) {
684 _val4 = AddOneToDecHex (_val4);
687 _val3 = AddOneToDecHex (_val3);
690 _val2 = AddOneToDecHex (_val2);
693 _val1 = AddOneToDecHex (_val1);
696 // Assume val != 0x99999999
697 private static uint AddOneToDecHex (uint val)
699 if ((val & 0xffff) == 0x9999)
700 if ((val & 0xffffff) == 0x999999)
701 if ((val & 0xfffffff) == 0x9999999)
702 return val + 0x06666667;
704 return val + 0x00666667;
705 else if ((val & 0xfffff) == 0x99999)
706 return val + 0x00066667;
708 return val + 0x00006667;
709 else if ((val & 0xff) == 0x99)
710 if ((val & 0xfff) == 0x999)
711 return val + 0x00000667;
713 return val + 0x00000067;
714 else if ((val & 0xf) == 0x9)
715 return val + 0x00000007;
720 private int CountTrailingZeros ()
723 return CountTrailingZeros (_val1);
725 return CountTrailingZeros (_val2) + 8;
727 return CountTrailingZeros (_val3) + 16;
729 return CountTrailingZeros (_val4) + 24;
733 private static int CountTrailingZeros (uint val)
735 if ((val & 0xffff) == 0)
736 if ((val & 0xffffff) == 0)
737 if ((val & 0xfffffff) == 0)
741 else if ((val & 0xfffff) == 0)
745 else if ((val & 0xff) == 0)
746 if ((val & 0xfff) == 0)
750 else if ((val & 0xf) == 0)
758 #region public number formatting methods
761 static NumberFormatter threadNumberFormatter;
764 static NumberFormatter userFormatProvider;
766 private static NumberFormatter GetInstance (IFormatProvider fp)
769 if (userFormatProvider == null) {
770 Interlocked.CompareExchange (ref userFormatProvider, new NumberFormatter (null), null);
773 return userFormatProvider;
776 NumberFormatter res = threadNumberFormatter;
777 threadNumberFormatter = null;
779 return new NumberFormatter (Thread.CurrentThread);
780 res.CurrentCulture = Thread.CurrentThread.CurrentCulture;
784 private void Release()
786 if (this != userFormatProvider)
787 threadNumberFormatter = this;
790 public static string NumberToString (string format, sbyte value, IFormatProvider fp)
792 NumberFormatter inst = GetInstance (fp);
793 inst.Init (format, value, Int8DefPrecision);
794 string res = inst.IntegerToString (format, fp);
799 public static string NumberToString (string format, byte value, IFormatProvider fp)
801 NumberFormatter inst = GetInstance (fp);
802 inst.Init (format, value, UInt8DefPrecision);
803 string res = inst.IntegerToString (format, fp);
808 public static string NumberToString (string format, ushort value, IFormatProvider fp)
810 NumberFormatter inst = GetInstance (fp);
811 inst.Init (format, value, Int16DefPrecision);
812 string res = inst.IntegerToString (format, fp);
817 public static string NumberToString (string format, short value, IFormatProvider fp)
819 NumberFormatter inst = GetInstance (fp);
820 inst.Init (format, value, UInt16DefPrecision);
821 string res = inst.IntegerToString (format, fp);
826 public static string NumberToString (string format, uint value, IFormatProvider fp)
828 NumberFormatter inst = GetInstance (fp);
829 inst.Init (format, value, Int32DefPrecision);
830 string res = inst.IntegerToString (format, fp);
835 public static string NumberToString (string format, int value, IFormatProvider fp)
837 NumberFormatter inst = GetInstance (fp);
838 inst.Init (format, value, UInt32DefPrecision);
839 string res = inst.IntegerToString (format, fp);
844 public static string NumberToString (string format, ulong value, IFormatProvider fp)
846 NumberFormatter inst = GetInstance (fp);
847 inst.Init (format, value);
848 string res = inst.IntegerToString (format, fp);
853 public static string NumberToString (string format, long value, IFormatProvider fp)
855 NumberFormatter inst = GetInstance (fp);
856 inst.Init (format, value);
857 string res = inst.IntegerToString (format, fp);
862 public static string NumberToString (string format, float value, IFormatProvider fp)
864 NumberFormatter inst = GetInstance (fp);
865 inst.Init (format, value, SingleDefPrecision);
866 NumberFormatInfo nfi = inst.GetNumberFormatInstance (fp);
870 else if (inst._infinity)
872 res = nfi.PositiveInfinitySymbol;
874 res = nfi.NegativeInfinitySymbol;
875 else if (inst._specifier == 'R')
876 res = inst.FormatRoundtrip (value, nfi);
878 res = inst.NumberToString (format, nfi);
883 public static string NumberToString (string format, double value, IFormatProvider fp)
885 NumberFormatter inst = GetInstance (fp);
886 inst.Init (format, value, DoubleDefPrecision);
887 NumberFormatInfo nfi = inst.GetNumberFormatInstance (fp);
891 else if (inst._infinity)
893 res = nfi.PositiveInfinitySymbol;
895 res = nfi.NegativeInfinitySymbol;
896 else if (inst._specifier == 'R')
897 res = inst.FormatRoundtrip (value, nfi);
899 res = inst.NumberToString (format, nfi);
904 public static string NumberToString (string format, decimal value, IFormatProvider fp)
906 NumberFormatter inst = GetInstance (fp);
907 inst.Init (format, value);
908 string res = inst.NumberToString (format, inst.GetNumberFormatInstance (fp));
913 public static string NumberToString (uint value, IFormatProvider fp)
915 if (value >= HundredMillion)
916 return NumberToString (null, value, fp);
918 NumberFormatter inst = GetInstance (fp);
919 string res = inst.FastIntegerToString ((int)value, fp);
924 public static string NumberToString (int value, IFormatProvider fp)
926 if (value >= HundredMillion || value <= -HundredMillion)
927 return NumberToString (null, value, fp);
929 NumberFormatter inst = GetInstance (fp);
930 string res = inst.FastIntegerToString (value, fp);
935 public static string NumberToString (ulong value, IFormatProvider fp)
937 if (value >= HundredMillion)
938 return NumberToString (null, value, fp);
940 NumberFormatter inst = GetInstance (fp);
941 string res = inst.FastIntegerToString ((int)value, fp);
946 public static string NumberToString (long value, IFormatProvider fp)
948 if (value >= HundredMillion || value <= -HundredMillion)
949 return NumberToString (null, value, fp);
951 NumberFormatter inst = GetInstance (fp);
952 string res = inst.FastIntegerToString ((int)value, fp);
957 public static string NumberToString (float value, IFormatProvider fp)
959 NumberFormatter inst = GetInstance (fp);
960 inst.Init (null, value, SingleDefPrecision);
961 NumberFormatInfo nfi = inst.GetNumberFormatInstance (fp);
965 else if (inst._infinity)
967 res = nfi.PositiveInfinitySymbol;
969 res = nfi.NegativeInfinitySymbol;
971 res = inst.FormatGeneral (-1, nfi);
976 public static string NumberToString (double value, IFormatProvider fp)
978 NumberFormatter inst = GetInstance (fp);
979 NumberFormatInfo nfi = inst.GetNumberFormatInstance (fp);
980 inst.Init (null, value, DoubleDefPrecision);
984 else if (inst._infinity)
986 res = nfi.PositiveInfinitySymbol;
988 res = nfi.NegativeInfinitySymbol;
990 res = inst.FormatGeneral (-1, nfi);
995 private string FastIntegerToString (int value, IFormatProvider fp)
998 string sign = GetNumberFormatInstance(fp).NegativeSign;
999 ResetCharBuf (8 + sign.Length);
1006 if (value >= 10000) {
1007 int v = value / 10000;
1008 FastAppendDigits (v, false);
1009 FastAppendDigits (value - v * 10000, true);
1012 FastAppendDigits (value, false);
1014 return new string (_cbuf, 0, _ind);
1017 private string IntegerToString (string format, IFormatProvider fp)
1019 NumberFormatInfo nfi = GetNumberFormatInstance (fp);
1020 switch (_specifier) {
1022 return FormatCurrency (_precision, nfi);
1024 return FormatDecimal (_precision, nfi);
1026 return FormatExponential (_precision, nfi);
1028 return FormatFixedPoint (_precision, nfi);
1030 if (_precision <= 0)
1031 return FormatDecimal (-1, nfi);
1032 return FormatGeneral (_precision, nfi);
1034 return FormatNumber (_precision, nfi);
1036 return FormatPercent (_precision, nfi);
1038 return FormatHexadecimal (_precision);
1040 if (_isCustomFormat)
1041 return FormatCustom (format, nfi);
1042 throw new FormatException ("The specified format '" + format + "' is invalid");
1046 private string NumberToString (string format, NumberFormatInfo nfi)
1048 switch (_specifier) {
1050 return FormatCurrency (_precision, nfi);
1052 return FormatExponential (_precision, nfi);
1054 return FormatFixedPoint (_precision, nfi);
1056 return FormatGeneral (_precision, nfi);
1058 return FormatNumber (_precision, nfi);
1060 return FormatPercent (_precision, nfi);
1063 if (_isCustomFormat)
1064 return FormatCustom (format, nfi);
1065 throw new FormatException ("The specified format '" + format + "' is invalid");
1069 public string FormatCurrency (int precision, NumberFormatInfo nfi)
1071 precision = (precision >= 0 ? precision : nfi.CurrencyDecimalDigits);
1072 RoundDecimal (precision);
1073 ResetCharBuf (IntegerDigits * 2 + precision * 2 + 16);
1076 switch (nfi.CurrencyPositivePattern) {
1078 Append (nfi.CurrencySymbol);
1081 Append (nfi.CurrencySymbol);
1087 switch (nfi.CurrencyNegativePattern) {
1090 Append (nfi.CurrencySymbol);
1093 Append (nfi.NegativeSign);
1094 Append (nfi.CurrencySymbol);
1097 Append (nfi.CurrencySymbol);
1098 Append (nfi.NegativeSign);
1101 Append (nfi.CurrencySymbol);
1107 Append (nfi.NegativeSign);
1110 Append (nfi.NegativeSign);
1113 Append (nfi.NegativeSign);
1114 Append (nfi.CurrencySymbol);
1118 Append (nfi.CurrencySymbol);
1122 Append (nfi.CurrencySymbol);
1124 Append (nfi.NegativeSign);
1128 Append (nfi.CurrencySymbol);
1137 AppendIntegerStringWithGroupSeparator (nfi.RawCurrencyGroupSizes, nfi.CurrencyGroupSeparator);
1139 if (precision > 0) {
1140 Append (nfi.CurrencyDecimalSeparator);
1141 AppendDecimalString (precision);
1145 switch (nfi.CurrencyPositivePattern) {
1147 Append (nfi.CurrencySymbol);
1151 Append (nfi.CurrencySymbol);
1156 switch (nfi.CurrencyNegativePattern) {
1161 Append (nfi.NegativeSign);
1164 Append (nfi.CurrencySymbol);
1168 Append (nfi.CurrencySymbol);
1171 Append (nfi.NegativeSign);
1172 Append (nfi.CurrencySymbol);
1175 Append (nfi.CurrencySymbol);
1176 Append (nfi.NegativeSign);
1180 Append (nfi.CurrencySymbol);
1184 Append (nfi.CurrencySymbol);
1185 Append (nfi.NegativeSign);
1188 Append (nfi.NegativeSign);
1191 Append (nfi.NegativeSign);
1193 Append (nfi.CurrencySymbol);
1200 Append (nfi.CurrencySymbol);
1206 return new string (_cbuf, 0, _ind);
1209 private string FormatDecimal (int precision, NumberFormatInfo nfi)
1211 if (precision < _digitsLen)
1212 precision = _digitsLen;
1216 ResetCharBuf (precision + 1);
1218 Append (nfi.NegativeSign);
1219 AppendDigits (0, precision);
1221 return new string (_cbuf, 0, _ind);
1224 unsafe private string FormatHexadecimal (int precision)
1226 int size = Math.Max (precision, _decPointPos);
1227 char* digits = _specifierIsUpper ? DigitUpperTable : DigitLowerTable;
1229 ResetCharBuf (size);
1231 ulong val = _val1 | ((ulong)_val2 << 32);
1233 _cbuf [--size] = digits [val & 0xf];
1236 return new string (_cbuf, 0, _ind);
1239 public string FormatFixedPoint (int precision, NumberFormatInfo nfi)
1241 if (precision == -1)
1242 precision = nfi.NumberDecimalDigits;
1244 RoundDecimal (precision);
1246 ResetCharBuf (IntegerDigits + precision + 2);
1249 Append (nfi.NegativeSign);
1251 AppendIntegerString (IntegerDigits);
1253 if (precision > 0) {
1254 Append (nfi.NumberDecimalSeparator);
1255 AppendDecimalString (precision);
1258 return new string (_cbuf, 0, _ind);
1261 private string FormatRoundtrip (double origval, NumberFormatInfo nfi)
1263 NumberFormatter nfc = GetClone ();
1264 if (origval >= MinRoundtripVal && origval <= MaxRoundtripVal) {
1265 string shortRep = FormatGeneral (_defPrecision, nfi);
1266 if (origval == Double.Parse (shortRep, nfi))
1269 return nfc.FormatGeneral (_defPrecision + 2, nfi);
1272 private string FormatRoundtrip (float origval, NumberFormatInfo nfi)
1274 NumberFormatter nfc = GetClone ();
1275 string shortRep = FormatGeneral (_defPrecision, nfi);
1276 // Check roundtrip only for "normal" double values.
1277 if (origval == Single.Parse (shortRep, nfi))
1279 return nfc.FormatGeneral (_defPrecision + 2, nfi);
1282 private string FormatGeneral (int precision, NumberFormatInfo nfi)
1285 if (precision == -1) {
1286 enableExp = IsFloatingSource;
1287 precision = _defPrecision;
1292 precision = _defPrecision;
1293 RoundPos (precision);
1296 int intDigits = _decPointPos;
1297 int digits = _digitsLen;
1298 int decDigits = digits - intDigits;
1300 if ((intDigits > precision || intDigits <= -4) && enableExp)
1301 return FormatExponential (digits - 1, nfi, 2);
1307 ResetCharBuf (decDigits + intDigits + 3);
1310 Append (nfi.NegativeSign);
1315 AppendDigits (digits - intDigits, digits);
1317 if (decDigits > 0) {
1318 Append (nfi.NumberDecimalSeparator);
1319 AppendDigits (0, decDigits);
1322 return new string (_cbuf, 0, _ind);
1325 public string FormatNumber (int precision, NumberFormatInfo nfi)
1327 precision = (precision >= 0 ? precision : nfi.NumberDecimalDigits);
1328 ResetCharBuf (IntegerDigits * 3 + precision);
1329 RoundDecimal (precision);
1332 switch (nfi.NumberNegativePattern) {
1337 Append (nfi.NegativeSign);
1340 Append (nfi.NegativeSign);
1346 AppendIntegerStringWithGroupSeparator (nfi.RawNumberGroupSizes, nfi.NumberGroupSeparator);
1348 if (precision > 0) {
1349 Append (nfi.NumberDecimalSeparator);
1350 AppendDecimalString (precision);
1354 switch (nfi.NumberNegativePattern) {
1359 Append (nfi.NegativeSign);
1363 Append (nfi.NegativeSign);
1368 return new string (_cbuf, 0, _ind);
1371 public string FormatPercent (int precision, NumberFormatInfo nfi)
1373 precision = (precision >= 0 ? precision : nfi.PercentDecimalDigits);
1375 RoundDecimal (precision);
1376 ResetCharBuf (IntegerDigits * 2 + precision + 16);
1379 if (nfi.PercentPositivePattern == 2)
1380 Append (nfi.PercentSymbol);
1383 switch (nfi.PercentNegativePattern) {
1385 Append (nfi.NegativeSign);
1388 Append (nfi.NegativeSign);
1391 Append (nfi.NegativeSign);
1392 Append (nfi.PercentSymbol);
1397 AppendIntegerStringWithGroupSeparator (nfi.RawPercentGroupSizes, nfi.PercentGroupSeparator);
1399 if (precision > 0) {
1400 Append (nfi.PercentDecimalSeparator);
1401 AppendDecimalString (precision);
1405 switch (nfi.PercentPositivePattern) {
1408 Append (nfi.PercentSymbol);
1411 Append (nfi.PercentSymbol);
1416 switch (nfi.PercentNegativePattern) {
1419 Append (nfi.PercentSymbol);
1422 Append (nfi.PercentSymbol);
1427 return new string (_cbuf, 0, _ind);
1430 public string FormatExponential (int precision, NumberFormatInfo nfi)
1432 if (precision == -1)
1433 precision = DefaultExpPrecision;
1435 RoundPos (precision + 1);
1436 return FormatExponential (precision, nfi, 3);
1439 private string FormatExponential (int precision, NumberFormatInfo nfi, int expDigits)
1441 int decDigits = _decPointPos;
1442 int digits = _digitsLen;
1443 int exponent = decDigits - 1;
1444 decDigits = _decPointPos = 1;
1446 ResetCharBuf (precision + 8);
1449 Append (nfi.NegativeSign);
1451 AppendOneDigit (digits - 1);
1453 if (precision > 0) {
1454 Append (nfi.NumberDecimalSeparator);
1455 AppendDigits (digits - precision - 1, digits - _decPointPos);
1458 AppendExponent (nfi, exponent, expDigits);
1460 return new string (_cbuf, 0, _ind);
1463 public string FormatCustom (string format, NumberFormatInfo nfi)
1468 CustomInfo.GetActiveSection (format, ref p, IsZero, ref offset, ref length);
1470 return _positive ? string.Empty : nfi.NegativeSign;
1473 CustomInfo info = CustomInfo.Parse (format, offset, length, nfi);
1475 Console.WriteLine ("Format : {0}",format);
1476 Console.WriteLine ("DecimalDigits : {0}",info.DecimalDigits);
1477 Console.WriteLine ("DecimalPointPos : {0}",info.DecimalPointPos);
1478 Console.WriteLine ("DecimalTailSharpDigits : {0}",info.DecimalTailSharpDigits);
1479 Console.WriteLine ("IntegerDigits : {0}",info.IntegerDigits);
1480 Console.WriteLine ("IntegerHeadSharpDigits : {0}",info.IntegerHeadSharpDigits);
1481 Console.WriteLine ("IntegerHeadPos : {0}",info.IntegerHeadPos);
1482 Console.WriteLine ("UseExponent : {0}",info.UseExponent);
1483 Console.WriteLine ("ExponentDigits : {0}",info.ExponentDigits);
1484 Console.WriteLine ("ExponentTailSharpDigits : {0}",info.ExponentTailSharpDigits);
1485 Console.WriteLine ("ExponentNegativeSignOnly : {0}",info.ExponentNegativeSignOnly);
1486 Console.WriteLine ("DividePlaces : {0}",info.DividePlaces);
1487 Console.WriteLine ("Percents : {0}",info.Percents);
1488 Console.WriteLine ("Permilles : {0}",info.Permilles);
1490 StringBuilder sb_int = new StringBuilder (info.IntegerDigits * 2);
1491 StringBuilder sb_dec = new StringBuilder (info.DecimalDigits * 2);
1492 StringBuilder sb_exp = (info.UseExponent ? new StringBuilder (info.ExponentDigits * 2) : null);
1495 if (info.Percents > 0)
1496 Multiply10(2 * info.Percents);
1497 if (info.Permilles > 0)
1498 Multiply10(3 * info.Permilles);
1499 if (info.DividePlaces > 0)
1500 Divide10(info.DividePlaces);
1502 bool expPositive = true;
1503 if (info.UseExponent && (info.DecimalDigits > 0 || info.IntegerDigits > 0)) {
1505 RoundPos (info.DecimalDigits + info.IntegerDigits);
1506 diff -= _decPointPos - info.IntegerDigits;
1507 _decPointPos = info.IntegerDigits;
1510 expPositive = diff <= 0;
1511 AppendNonNegativeNumber (sb_exp, diff < 0 ? -diff : diff);
1514 RoundDecimal (info.DecimalDigits);
1516 if (info.IntegerDigits != 0 || !IsZeroInteger)
1517 AppendIntegerString (IntegerDigits, sb_int);
1519 AppendDecimalString (DecimalDigits, sb_dec);
1521 if (info.UseExponent) {
1522 if (info.DecimalDigits <= 0 && info.IntegerDigits <= 0)
1525 if (sb_int.Length < info.IntegerDigits)
1526 sb_int.Insert (0, "0", info.IntegerDigits - sb_int.Length);
1528 while (sb_exp.Length < info.ExponentDigits - info.ExponentTailSharpDigits)
1529 sb_exp.Insert (0, '0');
1531 if (expPositive && !info.ExponentNegativeSignOnly)
1532 sb_exp.Insert (0, nfi.PositiveSign);
1533 else if (!expPositive)
1534 sb_exp.Insert (0, nfi.NegativeSign);
1537 if (sb_int.Length < info.IntegerDigits - info.IntegerHeadSharpDigits)
1538 sb_int.Insert (0, "0", info.IntegerDigits - info.IntegerHeadSharpDigits - sb_int.Length);
1539 if (info.IntegerDigits == info.IntegerHeadSharpDigits && IsZeroOnly (sb_int))
1540 sb_int.Remove (0, sb_int.Length);
1543 ZeroTrimEnd (sb_dec, true);
1544 while (sb_dec.Length < info.DecimalDigits - info.DecimalTailSharpDigits)
1545 sb_dec.Append ('0');
1546 if (sb_dec.Length > info.DecimalDigits)
1547 sb_dec.Remove (info.DecimalDigits, sb_dec.Length - info.DecimalDigits);
1549 return info.Format (format, offset, length, nfi, _positive, sb_int, sb_dec, sb_exp);
1551 #endregion public number formatting methods
1553 #region StringBuilder formatting helpers
1555 private static void ZeroTrimEnd (StringBuilder sb, bool canEmpty)
1558 for (int i = sb.Length - 1; (canEmpty ? i >= 0 : i > 0); i--) {
1565 sb.Remove (sb.Length - len, len);
1568 private static bool IsZeroOnly (StringBuilder sb)
1570 for (int i = 0; i < sb.Length; i++)
1571 if (char.IsDigit (sb [i]) && sb [i] != '0')
1576 private static void AppendNonNegativeNumber (StringBuilder sb, int v)
1579 throw new ArgumentException ();
1581 int i = ScaleOrder (v) - 1;
1583 int n = v / (int)GetTenPowerOf (i);
1584 sb.Append ((char)('0' | n));
1585 v -= (int)GetTenPowerOf (i--) * n;
1589 #endregion StringBuilder formatting helpers
1591 #region Append helpers
1593 private void AppendIntegerString (int minLength, StringBuilder sb)
1595 if (_decPointPos <= 0) {
1596 sb.Append ('0', minLength);
1600 if (_decPointPos < minLength)
1601 sb.Append ('0', minLength - _decPointPos);
1603 AppendDigits (_digitsLen - _decPointPos, _digitsLen, sb);
1606 private void AppendIntegerString (int minLength)
1608 if (_decPointPos <= 0) {
1609 Append ('0', minLength);
1613 if (_decPointPos < minLength)
1614 Append ('0', minLength - _decPointPos);
1616 AppendDigits (_digitsLen - _decPointPos, _digitsLen);
1619 private void AppendDecimalString (int precision, StringBuilder sb)
1621 AppendDigits (_digitsLen - precision - _decPointPos, _digitsLen - _decPointPos, sb);
1624 private void AppendDecimalString (int precision)
1626 AppendDigits (_digitsLen - precision - _decPointPos, _digitsLen - _decPointPos);
1629 private void AppendIntegerStringWithGroupSeparator (int[] groups, string groupSeparator)
1631 if (IsZeroInteger) {
1638 for (int i = 0; i < groups.Length; i++) {
1639 total += groups [i];
1640 if (total <= _decPointPos)
1646 if (groups.Length > 0 && total > 0) {
1648 int groupSize = groups [groupIndex];
1649 int fraction = _decPointPos > total ? _decPointPos - total : 0;
1650 if (groupSize == 0) {
1651 while (groupIndex >= 0 && groups [groupIndex] == 0)
1654 groupSize = fraction > 0 ? fraction : groups [groupIndex];
1657 counter = groupSize;
1659 groupIndex += fraction / groupSize;
1660 counter = fraction % groupSize;
1662 counter = groupSize;
1667 if (total >= _decPointPos) {
1668 int lastGroupSize = groups [0];
1669 if (total > lastGroupSize) {
1670 int lastGroupDiff = -(lastGroupSize - _decPointPos);
1673 if (lastGroupDiff < lastGroupSize)
1674 counter = lastGroupDiff;
1675 else if (lastGroupSize > 0 && (lastGroupMod = _decPointPos % lastGroupSize) > 0)
1676 counter = lastGroupMod;
1680 for (int i = 0; ;) {
1681 if ((_decPointPos - i) <= counter || counter == 0) {
1682 AppendDigits (_digitsLen - _decPointPos, _digitsLen - i);
1685 AppendDigits (_digitsLen - i - counter, _digitsLen - i);
1687 Append (groupSeparator);
1688 if (--groupIndex < groups.Length && groupIndex >= 0)
1689 groupSize = groups [groupIndex];
1690 counter = groupSize;
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')
1707 Append (nfi.PositiveSign);
1709 Append (nfi.NegativeSign);
1710 exponent = -exponent;
1714 Append ('0', minDigits);
1715 else if (exponent < 10) {
1716 Append ('0', minDigits - 1);
1717 Append ((char)('0' | exponent));
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)
1739 else if (start < 16)
1741 else if (start < 24)
1743 else if (start < 32)
1747 v >>= (start & 0x7) << 2;
1748 _cbuf [_ind++] = (char)('0' | v & 0xf);
1751 unsafe private void FastAppendDigits (int val, bool force)
1755 if (force || val >= 100) {
1756 int v = (val * 5243) >> 19;
1757 digits = DecHexDigits [v];
1758 if (force || val >= 1000)
1759 _cbuf [i++] = (char)('0' | digits >> 4);
1760 _cbuf [i++] = (char)('0' | (digits & 0xf));
1761 digits = DecHexDigits [val - v * 100];
1764 digits = DecHexDigits [val];
1766 if (force || val >= 10)
1767 _cbuf [i++] = (char)('0' | digits >> 4);
1768 _cbuf [i++] = (char)('0' | (digits & 0xf));
1772 private void AppendDigits (int start, int end)
1777 int i = _ind + (end - start);
1778 if (i > _cbuf.Length)
1785 for (int next = start + 8 - (start & 0x7); ; start = next, next += 8) {
1789 else if (next == 16)
1791 else if (next == 24)
1793 else if (next == 32)
1797 v >>= (start & 0x7) << 2;
1801 _cbuf [--i] = (char)('0' | v & 0xf);
1802 switch (next - start) {
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);
1822 _cbuf [--i] = (char)('0' | (v >>= 4) & 0xf);
1832 private void AppendDigits (int start, int end, StringBuilder sb)
1837 int i = sb.Length + (end - start);
1843 for (int next = start + 8 - (start & 0x7); ; start = next, next += 8) {
1847 else if (next == 16)
1849 else if (next == 24)
1851 else if (next == 32)
1855 v >>= (start & 0x7) << 2;
1858 sb [--i] = (char)('0' | v & 0xf);
1859 switch (next - start) {
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);
1879 sb [--i] = (char)('0' | (v >>= 4) & 0xf);
1889 #endregion Append helpers
1893 private void Multiply10(int count)
1895 if (count <= 0 || _digitsLen == 0)
1898 _decPointPos += count;
1901 private void Divide10(int count)
1903 if (count <= 0 || _digitsLen == 0)
1906 _decPointPos -= count;
1909 private NumberFormatter GetClone ()
1911 return (NumberFormatter)this.MemberwiseClone ();
1918 private class CustomInfo
1920 public bool UseGroup = false;
1921 public int DecimalDigits = 0;
1922 public int DecimalPointPos = -1;
1923 public int DecimalTailSharpDigits = 0;
1924 public int IntegerDigits = 0;
1925 public int IntegerHeadSharpDigits = 0;
1926 public int IntegerHeadPos = 0;
1927 public bool UseExponent = false;
1928 public int ExponentDigits = 0;
1929 public int ExponentTailSharpDigits = 0;
1930 public bool ExponentNegativeSignOnly = true;
1931 public int DividePlaces = 0;
1932 public int Percents = 0;
1933 public int Permilles = 0;
1935 public static void GetActiveSection (string format, ref bool positive, bool zero, ref int offset, ref int length)
1937 int[] lens = new int [3];
1940 bool quoted = false;
1942 for (int i = 0; i < format.Length; i++) {
1943 char c = format [i];
1945 if (c == '\"' || c == '\'') {
1946 if (i == 0 || format [i - 1] != '\\')
1952 if (c == ';' && !quoted && (i == 0 || format [i - 1] != '\\')) {
1953 lens [index++] = i - lastPos;
1962 length = format.Length;
1966 if (positive || zero) {
1971 if (lens [0] + 1 < format.Length) {
1973 offset = lens [0] + 1;
1974 length = format.Length - offset;
1985 offset = lens [0] + lens [1] + 2;
1986 length = format.Length - offset;
1996 offset = lens [0] + 1;
2008 offset = lens [0] + lens [1] + 2;
2019 offset = lens [0] + 1;
2030 throw new ArgumentException ();
2033 public static CustomInfo Parse (string format, int offset, int length, NumberFormatInfo nfi)
2035 char literal = '\0';
2036 bool integerArea = true;
2037 bool decimalArea = false;
2038 bool exponentArea = false;
2039 bool sharpContinues = true;
2041 CustomInfo info = new CustomInfo ();
2042 int groupSeparatorCounter = 0;
2044 for (int i = offset; i - offset < length; i++) {
2045 char c = format [i];
2047 if (c == literal && c != '\0') {
2051 if (literal != '\0')
2054 if (exponentArea && (c != '\0' && c != '0' && c != '#')) {
2055 exponentArea = false;
2056 integerArea = (info.DecimalPointPos < 0);
2057 decimalArea = !integerArea;
2068 if (c == '\"' || c == '\'') {
2073 if (sharpContinues && integerArea)
2074 info.IntegerHeadSharpDigits++;
2075 else if (decimalArea)
2076 info.DecimalTailSharpDigits++;
2077 else if (exponentArea)
2078 info.ExponentTailSharpDigits++;
2083 sharpContinues = false;
2085 info.DecimalTailSharpDigits = 0;
2086 else if (exponentArea)
2087 info.ExponentTailSharpDigits = 0;
2089 if (info.IntegerHeadPos == -1)
2090 info.IntegerHeadPos = i;
2093 info.IntegerDigits++;
2094 if (groupSeparatorCounter > 0)
2095 info.UseGroup = true;
2096 groupSeparatorCounter = 0;
2098 else if (decimalArea)
2099 info.DecimalDigits++;
2100 else if (exponentArea)
2101 info.ExponentDigits++;
2105 if (info.UseExponent)
2108 info.UseExponent = true;
2109 integerArea = false;
2110 decimalArea = false;
2111 exponentArea = true;
2112 if (i + 1 - offset < length) {
2113 char nc = format [i + 1];
2115 info.ExponentNegativeSignOnly = false;
2116 if (nc == '+' || nc == '-')
2118 else if (nc != '0' && nc != '#') {
2119 info.UseExponent = false;
2120 if (info.DecimalPointPos < 0)
2127 integerArea = false;
2129 exponentArea = false;
2130 if (info.DecimalPointPos == -1)
2131 info.DecimalPointPos = i;
2140 if (integerArea && info.IntegerDigits > 0)
2141 groupSeparatorCounter++;
2148 if (info.ExponentDigits == 0)
2149 info.UseExponent = false;
2151 info.IntegerHeadSharpDigits = 0;
2153 if (info.DecimalDigits == 0)
2154 info.DecimalPointPos = -1;
2156 info.DividePlaces += groupSeparatorCounter * 3;
2161 public string Format (string format, int offset, int length, NumberFormatInfo nfi, bool positive, StringBuilder sb_int, StringBuilder sb_dec, StringBuilder sb_exp)
2163 StringBuilder sb = new StringBuilder ();
2164 char literal = '\0';
2165 bool integerArea = true;
2166 bool decimalArea = false;
2167 int intSharpCounter = 0;
2168 int sb_int_index = 0;
2169 int sb_dec_index = 0;
2171 int[] groups = nfi.RawNumberGroupSizes;
2172 string groupSeparator = nfi.NumberGroupSeparator;
2173 int intLen = 0, total = 0, groupIndex = 0, counter = 0, groupSize = 0;
2174 if (UseGroup && groups.Length > 0) {
2175 intLen = sb_int.Length;
2176 for (int i = 0; i < groups.Length; i++) {
2177 total += groups [i];
2178 if (total <= intLen)
2181 groupSize = groups [groupIndex];
2182 int fraction = intLen > total ? intLen - total : 0;
2183 if (groupSize == 0) {
2184 while (groupIndex >= 0 && groups [groupIndex] == 0)
2187 groupSize = fraction > 0 ? fraction : groups [groupIndex];
2190 counter = groupSize;
2192 groupIndex += fraction / groupSize;
2193 counter = fraction % groupSize;
2195 counter = groupSize;
2203 for (int i = offset; i - offset < length; i++) {
2204 char c = format [i];
2206 if (c == literal && c != '\0') {
2210 if (literal != '\0') {
2218 if (i - offset < length)
2219 sb.Append (format [i]);
2223 if (c == '\"' || c == '\'')
2231 if (IntegerDigits - intSharpCounter < sb_int.Length + sb_int_index || c == '0')
2232 while (IntegerDigits - intSharpCounter + sb_int_index < sb_int.Length) {
2233 sb.Append (sb_int [sb_int_index++]);
2234 if (UseGroup && --intLen > 0 && --counter == 0) {
2235 sb.Append (groupSeparator);
2236 if (--groupIndex < groups.Length && groupIndex >= 0)
2237 groupSize = groups [groupIndex];
2238 counter = groupSize;
2243 else if (decimalArea) {
2244 if (sb_dec_index < sb_dec.Length)
2245 sb.Append (sb_dec [sb_dec_index++]);
2253 if (sb_exp == null || !UseExponent) {
2262 for (q = i + 1; q - offset < length; q++) {
2263 if (format [q] == '0') {
2267 if (q == i + 1 && (format [q] == '+' || format [q] == '-'))
2276 integerArea = (DecimalPointPos < 0);
2277 decimalArea = !integerArea;
2288 if (DecimalPointPos == i) {
2289 if (DecimalDigits > 0) {
2290 while (sb_int_index < sb_int.Length)
2291 sb.Append (sb_int [sb_int_index++]);
2293 if (sb_dec.Length > 0)
2294 sb.Append (nfi.NumberDecimalSeparator);
2296 integerArea = false;
2302 sb.Append (nfi.PercentSymbol);
2305 sb.Append (nfi.PerMilleSymbol);
2314 sb.Insert (0, nfi.NegativeSign);
2316 return sb.ToString ();