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 NumberFormatInfo _nfi;
111 //part of the private stringbuffer
112 private char[] _cbuf;
115 private bool _infinity;
116 private bool _isCustomFormat;
117 private bool _specifierIsUpper;
118 private bool _positive;
119 private char _specifier;
120 private int _precision;
121 private int _defPrecision;
123 private int _digitsLen;
124 private int _offset; // Represent the first digit offset.
125 private int _decPointPos;
127 // The following fields are a hexadeimal representation of the digits.
128 // For instance _val = 0x234 represents the digits '2', '3', '4'.
129 private uint _val1; // Digits 0 - 7.
130 private uint _val2; // Digits 8 - 15.
131 private uint _val3; // Digits 16 - 23.
132 private uint _val4; // Digits 23 - 31. Only needed for decimals.
136 #region Constructor Helpers
138 // Translate an unsigned int to hexadecimal digits.
139 // i.e. 123456789 is represented by _val1 = 0x23456789 and _val2 = 0x1
140 private void InitDecHexDigits (uint value)
142 if (value >= HundredMillion) {
143 int div1 = (int)(value / HundredMillion);
144 value -= HundredMillion * (uint)div1;
145 _val2 = FastToDecHex (div1);
147 _val1 = ToDecHex ((int)value);
150 // Translate an unsigned long to hexadecimal digits.
151 private void InitDecHexDigits (ulong value)
153 if (value >= HundredMillion) {
154 long div1 = (long)(value / HundredMillion);
155 value -= HundredMillion * (ulong)div1;
156 if (div1 >= HundredMillion) {
157 int div2 = (int)(div1 / HundredMillion);
158 div1 = div1 - div2 * (long)HundredMillion;
159 _val3 = ToDecHex (div2);
162 _val2 = ToDecHex ((int)(div1));
165 _val1 = ToDecHex ((int)value);
168 // Translate a decimal integer to hexadecimal digits.
169 // The decimal integer is 96 digits and its value is hi * 2^64 + lo.
170 // is the lower 64 bits.
171 private void InitDecHexDigits (uint hi, ulong lo)
174 InitDecHexDigits (lo); // Only the lower 64 bits matter.
178 // Compute (hi, lo) = (hi , lo) / HundredMillion.
179 uint divhi = hi / HundredMillion;
180 ulong remhi = hi - divhi * HundredMillion;
181 ulong divlo = lo / HundredMillion;
182 ulong remlo = lo - divlo * HundredMillion + remhi * ULongModHundredMillion;
184 lo = divlo + remhi * ULongDivHundredMillion;
185 divlo = remlo / HundredMillion;
186 remlo -= divlo * HundredMillion;
188 _val1 = ToDecHex ((int)remlo);
190 // Divide hi * 2 ^ 64 + lo by HundredMillion using the fact that
191 // hi < HundredMillion.
192 divlo = lo / HundredMillion;
193 remlo = lo - divlo * HundredMillion;
196 lo += hi * ULongDivHundredMillion;
197 remlo += hi * ULongModHundredMillion;
198 divlo = remlo / HundredMillion;
200 remlo -= divlo * HundredMillion;
202 _val2 = ToDecHex ((int)remlo);
204 // Now we are left with 64 bits store in lo.
205 if (lo >= HundredMillion) {
206 divlo = lo / HundredMillion;
207 lo -= divlo * HundredMillion;
208 _val4 = ToDecHex ((int)divlo);
210 _val3 = ToDecHex ((int)lo);
213 // Helper to translate an int in the range 0 .. 9999 to its
214 // Hexadecimal digits representation.
218 private static uint FastToDecHex (int val)
221 return (uint)DecHexDigits [val];
223 // Uses 2^19 (524288) to compute val / 100 for val < 10000.
224 int v = (val * 5243) >> 19;
225 return (uint)((DecHexDigits [v] << 8) | DecHexDigits [val - v * 100]);
228 // Helper to translate an int in the range 0 .. 99999999 to its
229 // Hexadecimal digits representation.
230 private static uint ToDecHex (int val)
236 res = FastToDecHex (v) << 16;
238 return res | FastToDecHex (val);
241 // Helper to count number of hexadecimal digits in a number.
242 private static int FastDecHexLen (int val)
249 else if (val < 0x1000)
255 private static int DecHexLen (uint val)
258 return FastDecHexLen ((int)val);
259 return 4 + FastDecHexLen ((int)(val >> 16));
262 // Count number of hexadecimal digits stored in _val1 .. _val4.
263 private int DecHexLen ()
266 return DecHexLen (_val4) + 24;
268 return DecHexLen (_val3) + 16;
270 return DecHexLen (_val2) + 8;
272 return DecHexLen (_val1);
277 // Helper to count the 10th scale (number of digits) in a number
278 private static int ScaleOrder (long hi)
280 for (int i = TenPowersListLength - 1; i >= 0; i--)
281 if (hi >= GetTenPowerOf (i))
286 // Compute the initial precision for rounding a floating number
287 // according to the used format.
288 int InitialFloatingPrecision ()
290 if (_specifier == 'R')
291 return _defPrecision + 2;
292 if (_precision < _defPrecision)
293 return _defPrecision;
294 if (_specifier == 'G')
295 return Math.Min (_defPrecision + 2, _precision);
296 if (_specifier == 'E')
297 return Math.Min (_defPrecision + 2, _precision + 1);
298 return _defPrecision;
301 // Parse the given format and extract the precision in it.
302 // Returns -1 for empty formats and -2 to indicate that the format
303 // is a custom format.
304 private static int ParsePrecision (string format)
307 for (int i = 1; i < format.Length; i++) {
308 int val = format [i] - '0';
309 precision = precision * 10 + val;
310 if (val < 0 || val > 9 || precision > 99)
316 #endregion Constructor Helpers
320 // Parse the given format and initialize the following fields:
321 // _isCustomFormat, _specifierIsUpper, _specifier & _precision.
322 public NumberFormatter (Thread current)
324 _cbuf = EmptyArray<char>.Value;
327 CurrentCulture = current.CurrentCulture;
330 private void Init (string format)
332 _val1 = _val2 = _val3 = _val4 = 0;
334 _NaN = _infinity = false;
335 _isCustomFormat = false;
336 _specifierIsUpper = true;
339 if (format == null || format.Length == 0) {
344 char specifier = format [0];
345 if (specifier >= 'a' && specifier <= 'z') {
346 specifier = (char)(specifier - 'a' + 'A');
347 _specifierIsUpper = false;
349 else if (specifier < 'A' || specifier > 'Z') {
350 _isCustomFormat = true;
354 _specifier = specifier;
355 if (format.Length > 1) {
356 _precision = ParsePrecision (format);
357 if (_precision == -2) { // Is it a custom format?
358 _isCustomFormat = true;
365 private void InitHex (ulong value)
367 switch (_defPrecision) {
368 case Int8DefPrecision: value = (byte) value; break;
369 case Int16DefPrecision: value = (ushort) value; break;
370 case Int32DefPrecision: value = (uint) value; break;
373 _val2 = (uint)(value >> 32);
374 _decPointPos = _digitsLen = DecHexLen ();
379 private void Init (string format, int value, int defPrecision)
382 _defPrecision = defPrecision;
383 _positive = value >= 0;
385 if (value == 0 || _specifier == 'X') {
386 InitHex ((ulong)value);
392 InitDecHexDigits ((uint)value);
393 _decPointPos = _digitsLen = DecHexLen ();
396 private void Init (string format, uint value, int defPrecision)
399 _defPrecision = defPrecision;
402 if (value == 0 || _specifier == 'X') {
407 InitDecHexDigits (value);
408 _decPointPos = _digitsLen = DecHexLen ();
411 private void Init (string format, long value)
414 _defPrecision = Int64DefPrecision;
415 _positive = value >= 0;
417 if (value == 0 || _specifier == 'X') {
418 InitHex ((ulong)value);
424 InitDecHexDigits ((ulong)value);
425 _decPointPos = _digitsLen = DecHexLen ();
428 private void Init (string format, ulong value)
431 _defPrecision = UInt64DefPrecision;
434 if (value == 0 || _specifier == 'X') {
435 InitHex ((ulong)value);
439 InitDecHexDigits (value);
440 _decPointPos = _digitsLen = DecHexLen ();
443 #if UNSAFE_TABLES // No unsafe code under TARGET_JVM
446 private void Init (string format, double value, int defPrecision)
450 _defPrecision = defPrecision;
451 long bits = BitConverter.DoubleToInt64Bits (value);
452 _positive = bits >= 0;
453 bits &= Int64.MaxValue;
461 int e = (int)(bits >> DoubleBitsExponentShift);
462 long m = bits & DoubleBitsMantissaMask;
463 if (e == DoubleBitsExponentMask) {
471 // We need 'm' to be large enough so we won't lose precision.
473 int scale = ScaleOrder (m);
474 if (scale < DoubleDefPrecision) {
475 expAdjust = scale - DoubleDefPrecision;
476 m *= GetTenPowerOf (-expAdjust);
480 m = (m + DoubleBitsMantissaMask + 1) * 10;
484 // multiply the mantissa by 10 ^ N
486 ulong hi = (ulong)m >> 32;
487 ulong lo2 = MantissaBitsTable [e];
488 ulong hi2 = lo2 >> 32;
490 ulong mm = hi * lo2 + lo * hi2 + ((lo * lo2) >> 32);
491 long res = (long)(hi * hi2 + (mm >> 32));
492 while (res < SeventeenDigitsThreshold) {
493 mm = (mm & UInt32.MaxValue) * 10;
494 res = res * 10 + (long)(mm >> 32);
497 if ((mm & 0x80000000) != 0)
500 int order = DoubleDefPrecision + 2;
501 _decPointPos = TensExponentTable [e] + expAdjust + order;
503 // Rescale 'res' to the initial precision (15-17 for doubles).
504 int initialPrecision = InitialFloatingPrecision ();
505 if (order > initialPrecision) {
506 long val = GetTenPowerOf (order - initialPrecision);
507 res = (res + (val >> 1)) / val;
508 order = initialPrecision;
510 if (res >= GetTenPowerOf (order)) {
515 InitDecHexDigits ((ulong)res);
516 _offset = CountTrailingZeros ();
517 _digitsLen = order - _offset;
520 private void Init (string format, decimal value)
523 _defPrecision = DecimalDefPrecision;
525 int[] bits = decimal.GetBits (value);
526 int scale = (bits [3] & DecimalBitsScaleMask) >> 16;
527 _positive = bits [3] >= 0;
528 if (bits [0] == 0 && bits [1] == 0 && bits [2] == 0) {
529 _decPointPos = -scale;
535 InitDecHexDigits ((uint)bits [2], ((ulong)bits [1] << 32) | (uint)bits [0]);
536 _digitsLen = DecHexLen ();
537 _decPointPos = _digitsLen - scale;
538 if (_precision != -1 || _specifier != 'G') {
539 _offset = CountTrailingZeros ();
540 _digitsLen -= _offset;
544 #endregion Constructors
546 #region Inner String Buffer
548 //_cbuf moved to before other fields to improve layout
551 private void ResetCharBuf (int size)
554 if (_cbuf.Length < size)
555 _cbuf = new char [size];
558 private void Resize (int len)
560 Array.Resize (ref _cbuf, len);
563 private void Append (char c)
565 if (_ind == _cbuf.Length)
570 private void Append (char c, int cnt)
572 if (_ind + cnt > _cbuf.Length)
573 Resize (_ind + cnt + 10);
578 private void Append (string s)
581 if (_ind + slen > _cbuf.Length)
582 Resize (_ind + slen + 10);
583 for (int i = 0; i < slen; i++)
584 _cbuf [_ind++] = s [i];
587 #endregion Inner String Buffer
589 #region Helper properties
591 private NumberFormatInfo GetNumberFormatInstance (IFormatProvider fp)
593 if (_nfi != null && fp == null)
595 return NumberFormatInfo.GetInstance (fp);
598 public CultureInfo CurrentCulture {
600 if (value != null && value.IsReadOnly)
601 _nfi = value.NumberFormat;
607 private int IntegerDigits {
608 get { return _decPointPos > 0 ? _decPointPos : 1; }
611 private int DecimalDigits {
612 get { return _digitsLen > _decPointPos ? _digitsLen - _decPointPos : 0; }
615 private bool IsFloatingSource {
616 get { return _defPrecision == DoubleDefPrecision || _defPrecision == SingleDefPrecision; }
619 private bool IsZero {
620 get { return _digitsLen == 0; }
623 private bool IsZeroInteger {
624 get { return _digitsLen == 0 || _decPointPos <= 0; }
627 #endregion Helper properties
631 private void RoundPos (int pos)
633 RoundBits (_digitsLen - pos);
636 private bool RoundDecimal (int decimals)
638 return RoundBits (_digitsLen - _decPointPos - decimals);
641 private bool RoundBits (int shift)
646 if (shift > _digitsLen) {
649 _val1 = _val2 = _val3 = _val4 = 0;
654 _digitsLen += _offset;
663 shift = (shift - 1) << 2;
664 uint v = _val1 >> shift;
665 uint rem16 = v & 0xf;
666 _val1 = (v ^ rem16) << shift;
669 _val1 |= 0x99999999 >> (28 - shift);
671 int newlen = DecHexLen ();
672 res = newlen != _digitsLen;
673 _decPointPos = _decPointPos + newlen - _digitsLen;
676 RemoveTrailingZeros ();
680 private void RemoveTrailingZeros ()
682 _offset = CountTrailingZeros ();
683 _digitsLen -= _offset;
684 if (_digitsLen == 0) {
691 private void AddOneToDecHex ()
693 if (_val1 == 0x99999999) {
695 if (_val2 == 0x99999999) {
697 if (_val3 == 0x99999999) {
699 _val4 = AddOneToDecHex (_val4);
702 _val3 = AddOneToDecHex (_val3);
705 _val2 = AddOneToDecHex (_val2);
708 _val1 = AddOneToDecHex (_val1);
711 // Assume val != 0x99999999
712 private static uint AddOneToDecHex (uint val)
714 if ((val & 0xffff) == 0x9999)
715 if ((val & 0xffffff) == 0x999999)
716 if ((val & 0xfffffff) == 0x9999999)
717 return val + 0x06666667;
719 return val + 0x00666667;
720 else if ((val & 0xfffff) == 0x99999)
721 return val + 0x00066667;
723 return val + 0x00006667;
724 else if ((val & 0xff) == 0x99)
725 if ((val & 0xfff) == 0x999)
726 return val + 0x00000667;
728 return val + 0x00000067;
729 else if ((val & 0xf) == 0x9)
730 return val + 0x00000007;
735 private int CountTrailingZeros ()
738 return CountTrailingZeros (_val1);
740 return CountTrailingZeros (_val2) + 8;
742 return CountTrailingZeros (_val3) + 16;
744 return CountTrailingZeros (_val4) + 24;
748 private static int CountTrailingZeros (uint val)
750 if ((val & 0xffff) == 0)
751 if ((val & 0xffffff) == 0)
752 if ((val & 0xfffffff) == 0)
756 else if ((val & 0xfffff) == 0)
760 else if ((val & 0xff) == 0)
761 if ((val & 0xfff) == 0)
765 else if ((val & 0xf) == 0)
773 #region public number formatting methods
776 static NumberFormatter threadNumberFormatter;
779 static NumberFormatter userFormatProvider;
781 private static NumberFormatter GetInstance (IFormatProvider fp)
784 if (userFormatProvider == null) {
785 Interlocked.CompareExchange (ref userFormatProvider, new NumberFormatter (null), null);
788 return userFormatProvider;
791 NumberFormatter res = threadNumberFormatter;
792 threadNumberFormatter = null;
794 return new NumberFormatter (Thread.CurrentThread);
795 res.CurrentCulture = Thread.CurrentThread.CurrentCulture;
799 private void Release()
801 if (this != userFormatProvider)
802 threadNumberFormatter = this;
805 public static string NumberToString (string format, sbyte value, IFormatProvider fp)
807 NumberFormatter inst = GetInstance (fp);
808 inst.Init (format, value, Int8DefPrecision);
809 string res = inst.IntegerToString (format, fp);
814 public static string NumberToString (string format, byte value, IFormatProvider fp)
816 NumberFormatter inst = GetInstance (fp);
817 inst.Init (format, value, UInt8DefPrecision);
818 string res = inst.IntegerToString (format, fp);
823 public static string NumberToString (string format, ushort value, IFormatProvider fp)
825 NumberFormatter inst = GetInstance (fp);
826 inst.Init (format, value, Int16DefPrecision);
827 string res = inst.IntegerToString (format, fp);
832 public static string NumberToString (string format, short value, IFormatProvider fp)
834 NumberFormatter inst = GetInstance (fp);
835 inst.Init (format, value, UInt16DefPrecision);
836 string res = inst.IntegerToString (format, fp);
841 public static string NumberToString (string format, uint value, IFormatProvider fp)
843 NumberFormatter inst = GetInstance (fp);
844 inst.Init (format, value, Int32DefPrecision);
845 string res = inst.IntegerToString (format, fp);
850 public static string NumberToString (string format, int value, IFormatProvider fp)
852 NumberFormatter inst = GetInstance (fp);
853 inst.Init (format, value, UInt32DefPrecision);
854 string res = inst.IntegerToString (format, fp);
859 public static string NumberToString (string format, ulong value, IFormatProvider fp)
861 NumberFormatter inst = GetInstance (fp);
862 inst.Init (format, value);
863 string res = inst.IntegerToString (format, fp);
868 public static string NumberToString (string format, long value, IFormatProvider fp)
870 NumberFormatter inst = GetInstance (fp);
871 inst.Init (format, value);
872 string res = inst.IntegerToString (format, fp);
877 public static string NumberToString (string format, float value, IFormatProvider fp)
879 NumberFormatter inst = GetInstance (fp);
880 inst.Init (format, value, SingleDefPrecision);
881 NumberFormatInfo nfi = inst.GetNumberFormatInstance (fp);
885 else if (inst._infinity)
887 res = nfi.PositiveInfinitySymbol;
889 res = nfi.NegativeInfinitySymbol;
890 else if (inst._specifier == 'R')
891 res = inst.FormatRoundtrip (value, nfi);
893 res = inst.NumberToString (format, nfi);
898 public static string NumberToString (string format, double value, IFormatProvider fp)
900 NumberFormatter inst = GetInstance (fp);
901 inst.Init (format, value, DoubleDefPrecision);
902 NumberFormatInfo nfi = inst.GetNumberFormatInstance (fp);
906 else if (inst._infinity)
908 res = nfi.PositiveInfinitySymbol;
910 res = nfi.NegativeInfinitySymbol;
911 else if (inst._specifier == 'R')
912 res = inst.FormatRoundtrip (value, nfi);
914 res = inst.NumberToString (format, nfi);
919 public static string NumberToString (string format, decimal value, IFormatProvider fp)
921 NumberFormatter inst = GetInstance (fp);
922 inst.Init (format, value);
923 string res = inst.NumberToString (format, inst.GetNumberFormatInstance (fp));
928 public static string NumberToString (uint value, IFormatProvider fp)
930 if (value >= HundredMillion)
931 return NumberToString (null, value, fp);
933 NumberFormatter inst = GetInstance (fp);
934 string res = inst.FastIntegerToString ((int)value, fp);
939 public static string NumberToString (int value, IFormatProvider fp)
941 if (value >= HundredMillion || value <= -HundredMillion)
942 return NumberToString (null, value, fp);
944 NumberFormatter inst = GetInstance (fp);
945 string res = inst.FastIntegerToString (value, fp);
950 public static string NumberToString (ulong value, IFormatProvider fp)
952 if (value >= HundredMillion)
953 return NumberToString (null, value, fp);
955 NumberFormatter inst = GetInstance (fp);
956 string res = inst.FastIntegerToString ((int)value, fp);
961 public static string NumberToString (long value, IFormatProvider fp)
963 if (value >= HundredMillion || value <= -HundredMillion)
964 return NumberToString (null, value, fp);
966 NumberFormatter inst = GetInstance (fp);
967 string res = inst.FastIntegerToString ((int)value, fp);
972 public static string NumberToString (float value, IFormatProvider fp)
974 NumberFormatter inst = GetInstance (fp);
975 inst.Init (null, value, SingleDefPrecision);
976 NumberFormatInfo nfi = inst.GetNumberFormatInstance (fp);
980 else if (inst._infinity)
982 res = nfi.PositiveInfinitySymbol;
984 res = nfi.NegativeInfinitySymbol;
986 res = inst.FormatGeneral (-1, nfi);
991 public static string NumberToString (double value, IFormatProvider fp)
993 NumberFormatter inst = GetInstance (fp);
994 NumberFormatInfo nfi = inst.GetNumberFormatInstance (fp);
995 inst.Init (null, value, DoubleDefPrecision);
999 else if (inst._infinity)
1001 res = nfi.PositiveInfinitySymbol;
1003 res = nfi.NegativeInfinitySymbol;
1005 res = inst.FormatGeneral (-1, nfi);
1010 private string FastIntegerToString (int value, IFormatProvider fp)
1013 string sign = GetNumberFormatInstance(fp).NegativeSign;
1014 ResetCharBuf (8 + sign.Length);
1021 if (value >= 10000) {
1022 int v = value / 10000;
1023 FastAppendDigits (v, false);
1024 FastAppendDigits (value - v * 10000, true);
1027 FastAppendDigits (value, false);
1029 return new string (_cbuf, 0, _ind);
1032 private string IntegerToString (string format, IFormatProvider fp)
1034 NumberFormatInfo nfi = GetNumberFormatInstance (fp);
1035 switch (_specifier) {
1037 return FormatCurrency (_precision, nfi);
1039 return FormatDecimal (_precision, nfi);
1041 return FormatExponential (_precision, nfi);
1043 return FormatFixedPoint (_precision, nfi);
1045 if (_precision <= 0)
1046 return FormatDecimal (-1, nfi);
1047 return FormatGeneral (_precision, nfi);
1049 return FormatNumber (_precision, nfi);
1051 return FormatPercent (_precision, nfi);
1053 return FormatHexadecimal (_precision);
1055 if (_isCustomFormat)
1056 return FormatCustom (format, nfi);
1057 throw new FormatException ("The specified format '" + format + "' is invalid");
1061 private string NumberToString (string format, NumberFormatInfo nfi)
1063 switch (_specifier) {
1065 return FormatCurrency (_precision, nfi);
1067 return FormatExponential (_precision, nfi);
1069 return FormatFixedPoint (_precision, nfi);
1071 return FormatGeneral (_precision, nfi);
1073 return FormatNumber (_precision, nfi);
1075 return FormatPercent (_precision, nfi);
1078 if (_isCustomFormat)
1079 return FormatCustom (format, nfi);
1080 throw new FormatException ("The specified format '" + format + "' is invalid");
1084 public string FormatCurrency (int precision, NumberFormatInfo nfi)
1086 precision = (precision >= 0 ? precision : nfi.CurrencyDecimalDigits);
1087 RoundDecimal (precision);
1088 ResetCharBuf (IntegerDigits * 2 + precision * 2 + 16);
1091 switch (nfi.CurrencyPositivePattern) {
1093 Append (nfi.CurrencySymbol);
1096 Append (nfi.CurrencySymbol);
1102 switch (nfi.CurrencyNegativePattern) {
1105 Append (nfi.CurrencySymbol);
1108 Append (nfi.NegativeSign);
1109 Append (nfi.CurrencySymbol);
1112 Append (nfi.CurrencySymbol);
1113 Append (nfi.NegativeSign);
1116 Append (nfi.CurrencySymbol);
1122 Append (nfi.NegativeSign);
1125 Append (nfi.NegativeSign);
1128 Append (nfi.NegativeSign);
1129 Append (nfi.CurrencySymbol);
1133 Append (nfi.CurrencySymbol);
1137 Append (nfi.CurrencySymbol);
1139 Append (nfi.NegativeSign);
1143 Append (nfi.CurrencySymbol);
1152 AppendIntegerStringWithGroupSeparator (nfi.RawCurrencyGroupSizes, nfi.CurrencyGroupSeparator);
1154 if (precision > 0) {
1155 Append (nfi.CurrencyDecimalSeparator);
1156 AppendDecimalString (precision);
1160 switch (nfi.CurrencyPositivePattern) {
1162 Append (nfi.CurrencySymbol);
1166 Append (nfi.CurrencySymbol);
1171 switch (nfi.CurrencyNegativePattern) {
1176 Append (nfi.NegativeSign);
1179 Append (nfi.CurrencySymbol);
1183 Append (nfi.CurrencySymbol);
1186 Append (nfi.NegativeSign);
1187 Append (nfi.CurrencySymbol);
1190 Append (nfi.CurrencySymbol);
1191 Append (nfi.NegativeSign);
1195 Append (nfi.CurrencySymbol);
1199 Append (nfi.CurrencySymbol);
1200 Append (nfi.NegativeSign);
1203 Append (nfi.NegativeSign);
1206 Append (nfi.NegativeSign);
1208 Append (nfi.CurrencySymbol);
1215 Append (nfi.CurrencySymbol);
1221 return new string (_cbuf, 0, _ind);
1224 private string FormatDecimal (int precision, NumberFormatInfo nfi)
1226 if (precision < _digitsLen)
1227 precision = _digitsLen;
1231 ResetCharBuf (precision + 1);
1233 Append (nfi.NegativeSign);
1234 AppendDigits (0, precision);
1236 return new string (_cbuf, 0, _ind);
1239 #if UNSAFE_TABLES // No unsafe code under TARGET_JVM
1242 private string FormatHexadecimal (int precision)
1244 int size = Math.Max (precision, _decPointPos);
1246 char* digits = _specifierIsUpper ? DigitUpperTable : DigitLowerTable;
1248 char[] digits = _specifierIsUpper ? DigitUpperTable : DigitLowerTable;
1250 ResetCharBuf (size);
1252 ulong val = _val1 | ((ulong)_val2 << 32);
1254 _cbuf [--size] = digits [val & 0xf];
1257 return new string (_cbuf, 0, _ind);
1260 public string FormatFixedPoint (int precision, NumberFormatInfo nfi)
1262 if (precision == -1)
1263 precision = nfi.NumberDecimalDigits;
1265 RoundDecimal (precision);
1267 ResetCharBuf (IntegerDigits + precision + 2);
1270 Append (nfi.NegativeSign);
1272 AppendIntegerString (IntegerDigits);
1274 if (precision > 0) {
1275 Append (nfi.NumberDecimalSeparator);
1276 AppendDecimalString (precision);
1279 return new string (_cbuf, 0, _ind);
1282 private string FormatRoundtrip (double origval, NumberFormatInfo nfi)
1284 NumberFormatter nfc = GetClone ();
1285 if (origval >= MinRoundtripVal && origval <= MaxRoundtripVal) {
1286 string shortRep = FormatGeneral (_defPrecision, nfi);
1287 if (origval == Double.Parse (shortRep, nfi))
1290 return nfc.FormatGeneral (_defPrecision + 2, nfi);
1293 private string FormatRoundtrip (float origval, NumberFormatInfo nfi)
1295 NumberFormatter nfc = GetClone ();
1296 string shortRep = FormatGeneral (_defPrecision, nfi);
1297 // Check roundtrip only for "normal" double values.
1298 if (origval == Single.Parse (shortRep, nfi))
1300 return nfc.FormatGeneral (_defPrecision + 2, nfi);
1303 private string FormatGeneral (int precision, NumberFormatInfo nfi)
1306 if (precision == -1) {
1307 enableExp = IsFloatingSource;
1308 precision = _defPrecision;
1313 precision = _defPrecision;
1314 RoundPos (precision);
1317 int intDigits = _decPointPos;
1318 int digits = _digitsLen;
1319 int decDigits = digits - intDigits;
1321 if ((intDigits > precision || intDigits <= -4) && enableExp)
1322 return FormatExponential (digits - 1, nfi, 2);
1328 ResetCharBuf (decDigits + intDigits + 3);
1331 Append (nfi.NegativeSign);
1336 AppendDigits (digits - intDigits, digits);
1338 if (decDigits > 0) {
1339 Append (nfi.NumberDecimalSeparator);
1340 AppendDigits (0, decDigits);
1343 return new string (_cbuf, 0, _ind);
1346 public string FormatNumber (int precision, NumberFormatInfo nfi)
1348 precision = (precision >= 0 ? precision : nfi.NumberDecimalDigits);
1349 ResetCharBuf (IntegerDigits * 3 + precision);
1350 RoundDecimal (precision);
1353 switch (nfi.NumberNegativePattern) {
1358 Append (nfi.NegativeSign);
1361 Append (nfi.NegativeSign);
1367 AppendIntegerStringWithGroupSeparator (nfi.RawNumberGroupSizes, nfi.NumberGroupSeparator);
1369 if (precision > 0) {
1370 Append (nfi.NumberDecimalSeparator);
1371 AppendDecimalString (precision);
1375 switch (nfi.NumberNegativePattern) {
1380 Append (nfi.NegativeSign);
1384 Append (nfi.NegativeSign);
1389 return new string (_cbuf, 0, _ind);
1392 public string FormatPercent (int precision, NumberFormatInfo nfi)
1394 precision = (precision >= 0 ? precision : nfi.PercentDecimalDigits);
1396 RoundDecimal (precision);
1397 ResetCharBuf (IntegerDigits * 2 + precision + 16);
1400 if (nfi.PercentPositivePattern == 2)
1401 Append (nfi.PercentSymbol);
1404 switch (nfi.PercentNegativePattern) {
1406 Append (nfi.NegativeSign);
1409 Append (nfi.NegativeSign);
1412 Append (nfi.NegativeSign);
1413 Append (nfi.PercentSymbol);
1418 AppendIntegerStringWithGroupSeparator (nfi.RawPercentGroupSizes, nfi.PercentGroupSeparator);
1420 if (precision > 0) {
1421 Append (nfi.PercentDecimalSeparator);
1422 AppendDecimalString (precision);
1426 switch (nfi.PercentPositivePattern) {
1429 Append (nfi.PercentSymbol);
1432 Append (nfi.PercentSymbol);
1437 switch (nfi.PercentNegativePattern) {
1440 Append (nfi.PercentSymbol);
1443 Append (nfi.PercentSymbol);
1448 return new string (_cbuf, 0, _ind);
1451 public string FormatExponential (int precision, NumberFormatInfo nfi)
1453 if (precision == -1)
1454 precision = DefaultExpPrecision;
1456 RoundPos (precision + 1);
1457 return FormatExponential (precision, nfi, 3);
1460 private string FormatExponential (int precision, NumberFormatInfo nfi, int expDigits)
1462 int decDigits = _decPointPos;
1463 int digits = _digitsLen;
1464 int exponent = decDigits - 1;
1465 decDigits = _decPointPos = 1;
1467 ResetCharBuf (precision + 8);
1470 Append (nfi.NegativeSign);
1472 AppendOneDigit (digits - 1);
1474 if (precision > 0) {
1475 Append (nfi.NumberDecimalSeparator);
1476 AppendDigits (digits - precision - 1, digits - _decPointPos);
1479 AppendExponent (nfi, exponent, expDigits);
1481 return new string (_cbuf, 0, _ind);
1484 public string FormatCustom (string format, NumberFormatInfo nfi)
1489 CustomInfo.GetActiveSection (format, ref p, IsZero, ref offset, ref length);
1491 return _positive ? string.Empty : nfi.NegativeSign;
1494 CustomInfo info = CustomInfo.Parse (format, offset, length, nfi);
1496 Console.WriteLine ("Format : {0}",format);
1497 Console.WriteLine ("DecimalDigits : {0}",info.DecimalDigits);
1498 Console.WriteLine ("DecimalPointPos : {0}",info.DecimalPointPos);
1499 Console.WriteLine ("DecimalTailSharpDigits : {0}",info.DecimalTailSharpDigits);
1500 Console.WriteLine ("IntegerDigits : {0}",info.IntegerDigits);
1501 Console.WriteLine ("IntegerHeadSharpDigits : {0}",info.IntegerHeadSharpDigits);
1502 Console.WriteLine ("IntegerHeadPos : {0}",info.IntegerHeadPos);
1503 Console.WriteLine ("UseExponent : {0}",info.UseExponent);
1504 Console.WriteLine ("ExponentDigits : {0}",info.ExponentDigits);
1505 Console.WriteLine ("ExponentTailSharpDigits : {0}",info.ExponentTailSharpDigits);
1506 Console.WriteLine ("ExponentNegativeSignOnly : {0}",info.ExponentNegativeSignOnly);
1507 Console.WriteLine ("DividePlaces : {0}",info.DividePlaces);
1508 Console.WriteLine ("Percents : {0}",info.Percents);
1509 Console.WriteLine ("Permilles : {0}",info.Permilles);
1511 StringBuilder sb_int = new StringBuilder (info.IntegerDigits * 2);
1512 StringBuilder sb_dec = new StringBuilder (info.DecimalDigits * 2);
1513 StringBuilder sb_exp = (info.UseExponent ? new StringBuilder (info.ExponentDigits * 2) : null);
1516 if (info.Percents > 0)
1517 Multiply10(2 * info.Percents);
1518 if (info.Permilles > 0)
1519 Multiply10(3 * info.Permilles);
1520 if (info.DividePlaces > 0)
1521 Divide10(info.DividePlaces);
1523 bool expPositive = true;
1524 if (info.UseExponent && (info.DecimalDigits > 0 || info.IntegerDigits > 0)) {
1526 RoundPos (info.DecimalDigits + info.IntegerDigits);
1527 diff -= _decPointPos - info.IntegerDigits;
1528 _decPointPos = info.IntegerDigits;
1531 expPositive = diff <= 0;
1532 AppendNonNegativeNumber (sb_exp, diff < 0 ? -diff : diff);
1535 RoundDecimal (info.DecimalDigits);
1537 if (info.IntegerDigits != 0 || !IsZeroInteger)
1538 AppendIntegerString (IntegerDigits, sb_int);
1540 AppendDecimalString (DecimalDigits, sb_dec);
1542 if (info.UseExponent) {
1543 if (info.DecimalDigits <= 0 && info.IntegerDigits <= 0)
1546 if (sb_int.Length < info.IntegerDigits)
1547 sb_int.Insert (0, "0", info.IntegerDigits - sb_int.Length);
1549 while (sb_exp.Length < info.ExponentDigits - info.ExponentTailSharpDigits)
1550 sb_exp.Insert (0, '0');
1552 if (expPositive && !info.ExponentNegativeSignOnly)
1553 sb_exp.Insert (0, nfi.PositiveSign);
1554 else if (!expPositive)
1555 sb_exp.Insert (0, nfi.NegativeSign);
1558 if (sb_int.Length < info.IntegerDigits - info.IntegerHeadSharpDigits)
1559 sb_int.Insert (0, "0", info.IntegerDigits - info.IntegerHeadSharpDigits - sb_int.Length);
1560 if (info.IntegerDigits == info.IntegerHeadSharpDigits && IsZeroOnly (sb_int))
1561 sb_int.Remove (0, sb_int.Length);
1564 ZeroTrimEnd (sb_dec, true);
1565 while (sb_dec.Length < info.DecimalDigits - info.DecimalTailSharpDigits)
1566 sb_dec.Append ('0');
1567 if (sb_dec.Length > info.DecimalDigits)
1568 sb_dec.Remove (info.DecimalDigits, sb_dec.Length - info.DecimalDigits);
1570 return info.Format (format, offset, length, nfi, _positive, sb_int, sb_dec, sb_exp);
1572 #endregion public number formatting methods
1574 #region StringBuilder formatting helpers
1576 private static void ZeroTrimEnd (StringBuilder sb, bool canEmpty)
1579 for (int i = sb.Length - 1; (canEmpty ? i >= 0 : i > 0); i--) {
1586 sb.Remove (sb.Length - len, len);
1589 private static bool IsZeroOnly (StringBuilder sb)
1591 for (int i = 0; i < sb.Length; i++)
1592 if (char.IsDigit (sb [i]) && sb [i] != '0')
1597 private static void AppendNonNegativeNumber (StringBuilder sb, int v)
1600 throw new ArgumentException ();
1602 int i = ScaleOrder (v) - 1;
1604 int n = v / (int)GetTenPowerOf (i);
1605 sb.Append ((char)('0' | n));
1606 v -= (int)GetTenPowerOf (i--) * n;
1610 #endregion StringBuilder formatting helpers
1612 #region Append helpers
1614 private void AppendIntegerString (int minLength, StringBuilder sb)
1616 if (_decPointPos <= 0) {
1617 sb.Append ('0', minLength);
1621 if (_decPointPos < minLength)
1622 sb.Append ('0', minLength - _decPointPos);
1624 AppendDigits (_digitsLen - _decPointPos, _digitsLen, sb);
1627 private void AppendIntegerString (int minLength)
1629 if (_decPointPos <= 0) {
1630 Append ('0', minLength);
1634 if (_decPointPos < minLength)
1635 Append ('0', minLength - _decPointPos);
1637 AppendDigits (_digitsLen - _decPointPos, _digitsLen);
1640 private void AppendDecimalString (int precision, StringBuilder sb)
1642 AppendDigits (_digitsLen - precision - _decPointPos, _digitsLen - _decPointPos, sb);
1645 private void AppendDecimalString (int precision)
1647 AppendDigits (_digitsLen - precision - _decPointPos, _digitsLen - _decPointPos);
1650 private void AppendIntegerStringWithGroupSeparator (int[] groups, string groupSeparator)
1652 if (IsZeroInteger) {
1659 for (int i = 0; i < groups.Length; i++) {
1660 total += groups [i];
1661 if (total <= _decPointPos)
1667 if (groups.Length > 0 && total > 0) {
1669 int groupSize = groups [groupIndex];
1670 int fraction = _decPointPos > total ? _decPointPos - total : 0;
1671 if (groupSize == 0) {
1672 while (groupIndex >= 0 && groups [groupIndex] == 0)
1675 groupSize = fraction > 0 ? fraction : groups [groupIndex];
1678 counter = groupSize;
1680 groupIndex += fraction / groupSize;
1681 counter = fraction % groupSize;
1683 counter = groupSize;
1688 if (total >= _decPointPos) {
1689 int lastGroupSize = groups [0];
1690 if (total > lastGroupSize) {
1691 int lastGroupDiff = -(lastGroupSize - _decPointPos);
1694 if (lastGroupDiff < lastGroupSize)
1695 counter = lastGroupDiff;
1696 else if (lastGroupSize > 0 && (lastGroupMod = _decPointPos % lastGroupSize) > 0)
1697 counter = lastGroupMod;
1701 for (int i = 0; ;) {
1702 if ((_decPointPos - i) <= counter || counter == 0) {
1703 AppendDigits (_digitsLen - _decPointPos, _digitsLen - i);
1706 AppendDigits (_digitsLen - i - counter, _digitsLen - i);
1708 Append (groupSeparator);
1709 if (--groupIndex < groups.Length && groupIndex >= 0)
1710 groupSize = groups [groupIndex];
1711 counter = groupSize;
1715 AppendDigits (_digitsLen - _decPointPos, _digitsLen);
1719 // minDigits is in the range 1..3
1720 private void AppendExponent (NumberFormatInfo nfi, int exponent, int minDigits)
1722 if (_specifierIsUpper || _specifier == 'R')
1728 Append (nfi.PositiveSign);
1730 Append (nfi.NegativeSign);
1731 exponent = -exponent;
1735 Append ('0', minDigits);
1736 else if (exponent < 10) {
1737 Append ('0', minDigits - 1);
1738 Append ((char)('0' | exponent));
1741 uint hexDigit = FastToDecHex (exponent);
1742 if (exponent >= 100 || minDigits == 3)
1743 Append ((char)('0' | (hexDigit >> 8)));
1744 Append ((char)('0' | ((hexDigit >> 4) & 0xf)));
1745 Append ((char)('0' | (hexDigit & 0xf)));
1749 private void AppendOneDigit (int start)
1751 if (_ind == _cbuf.Length)
1760 else if (start < 16)
1762 else if (start < 24)
1764 else if (start < 32)
1768 v >>= (start & 0x7) << 2;
1769 _cbuf [_ind++] = (char)('0' | v & 0xf);
1772 #if UNSAFE_TABLES // No unsafe code under TARGET_JVM
1775 private void FastAppendDigits (int val, bool force)
1779 if (force || val >= 100) {
1780 int v = (val * 5243) >> 19;
1781 digits = DecHexDigits [v];
1782 if (force || val >= 1000)
1783 _cbuf [i++] = (char)('0' | digits >> 4);
1784 _cbuf [i++] = (char)('0' | (digits & 0xf));
1785 digits = DecHexDigits [val - v * 100];
1788 digits = DecHexDigits [val];
1790 if (force || val >= 10)
1791 _cbuf [i++] = (char)('0' | digits >> 4);
1792 _cbuf [i++] = (char)('0' | (digits & 0xf));
1796 private void AppendDigits (int start, int end)
1801 int i = _ind + (end - start);
1802 if (i > _cbuf.Length)
1809 for (int next = start + 8 - (start & 0x7); ; start = next, next += 8) {
1813 else if (next == 16)
1815 else if (next == 24)
1817 else if (next == 32)
1821 v >>= (start & 0x7) << 2;
1825 _cbuf [--i] = (char)('0' | v & 0xf);
1826 switch (next - start) {
1828 _cbuf [--i] = (char)('0' | (v >>= 4) & 0xf);
1831 _cbuf [--i] = (char)('0' | (v >>= 4) & 0xf);
1834 _cbuf [--i] = (char)('0' | (v >>= 4) & 0xf);
1837 _cbuf [--i] = (char)('0' | (v >>= 4) & 0xf);
1840 _cbuf [--i] = (char)('0' | (v >>= 4) & 0xf);
1843 _cbuf [--i] = (char)('0' | (v >>= 4) & 0xf);
1846 _cbuf [--i] = (char)('0' | (v >>= 4) & 0xf);
1856 private void AppendDigits (int start, int end, StringBuilder sb)
1861 int i = sb.Length + (end - start);
1867 for (int next = start + 8 - (start & 0x7); ; start = next, next += 8) {
1871 else if (next == 16)
1873 else if (next == 24)
1875 else if (next == 32)
1879 v >>= (start & 0x7) << 2;
1882 sb [--i] = (char)('0' | v & 0xf);
1883 switch (next - start) {
1885 sb [--i] = (char)('0' | (v >>= 4) & 0xf);
1888 sb [--i] = (char)('0' | (v >>= 4) & 0xf);
1891 sb [--i] = (char)('0' | (v >>= 4) & 0xf);
1894 sb [--i] = (char)('0' | (v >>= 4) & 0xf);
1897 sb [--i] = (char)('0' | (v >>= 4) & 0xf);
1900 sb [--i] = (char)('0' | (v >>= 4) & 0xf);
1903 sb [--i] = (char)('0' | (v >>= 4) & 0xf);
1913 #endregion Append helpers
1917 private void Multiply10(int count)
1919 if (count <= 0 || _digitsLen == 0)
1922 _decPointPos += count;
1925 private void Divide10(int count)
1927 if (count <= 0 || _digitsLen == 0)
1930 _decPointPos -= count;
1933 private NumberFormatter GetClone ()
1935 return (NumberFormatter)this.MemberwiseClone ();
1942 private class CustomInfo
1944 public bool UseGroup = false;
1945 public int DecimalDigits = 0;
1946 public int DecimalPointPos = -1;
1947 public int DecimalTailSharpDigits = 0;
1948 public int IntegerDigits = 0;
1949 public int IntegerHeadSharpDigits = 0;
1950 public int IntegerHeadPos = 0;
1951 public bool UseExponent = false;
1952 public int ExponentDigits = 0;
1953 public int ExponentTailSharpDigits = 0;
1954 public bool ExponentNegativeSignOnly = true;
1955 public int DividePlaces = 0;
1956 public int Percents = 0;
1957 public int Permilles = 0;
1959 public static void GetActiveSection (string format, ref bool positive, bool zero, ref int offset, ref int length)
1961 int[] lens = new int [3];
1964 bool quoted = false;
1966 for (int i = 0; i < format.Length; i++) {
1967 char c = format [i];
1969 if (c == '\"' || c == '\'') {
1970 if (i == 0 || format [i - 1] != '\\')
1976 if (c == ';' && !quoted && (i == 0 || format [i - 1] != '\\')) {
1977 lens [index++] = i - lastPos;
1986 length = format.Length;
1990 if (positive || zero) {
1995 if (lens [0] + 1 < format.Length) {
1997 offset = lens [0] + 1;
1998 length = format.Length - offset;
2009 offset = lens [0] + lens [1] + 2;
2010 length = format.Length - offset;
2020 offset = lens [0] + 1;
2032 offset = lens [0] + lens [1] + 2;
2043 offset = lens [0] + 1;
2054 throw new ArgumentException ();
2057 public static CustomInfo Parse (string format, int offset, int length, NumberFormatInfo nfi)
2059 char literal = '\0';
2060 bool integerArea = true;
2061 bool decimalArea = false;
2062 bool exponentArea = false;
2063 bool sharpContinues = true;
2065 CustomInfo info = new CustomInfo ();
2066 int groupSeparatorCounter = 0;
2068 for (int i = offset; i - offset < length; i++) {
2069 char c = format [i];
2071 if (c == literal && c != '\0') {
2075 if (literal != '\0')
2078 if (exponentArea && (c != '\0' && c != '0' && c != '#')) {
2079 exponentArea = false;
2080 integerArea = (info.DecimalPointPos < 0);
2081 decimalArea = !integerArea;
2092 if (c == '\"' || c == '\'') {
2097 if (sharpContinues && integerArea)
2098 info.IntegerHeadSharpDigits++;
2099 else if (decimalArea)
2100 info.DecimalTailSharpDigits++;
2101 else if (exponentArea)
2102 info.ExponentTailSharpDigits++;
2107 sharpContinues = false;
2109 info.DecimalTailSharpDigits = 0;
2110 else if (exponentArea)
2111 info.ExponentTailSharpDigits = 0;
2113 if (info.IntegerHeadPos == -1)
2114 info.IntegerHeadPos = i;
2117 info.IntegerDigits++;
2118 if (groupSeparatorCounter > 0)
2119 info.UseGroup = true;
2120 groupSeparatorCounter = 0;
2122 else if (decimalArea)
2123 info.DecimalDigits++;
2124 else if (exponentArea)
2125 info.ExponentDigits++;
2129 if (info.UseExponent)
2132 info.UseExponent = true;
2133 integerArea = false;
2134 decimalArea = false;
2135 exponentArea = true;
2136 if (i + 1 - offset < length) {
2137 char nc = format [i + 1];
2139 info.ExponentNegativeSignOnly = false;
2140 if (nc == '+' || nc == '-')
2142 else if (nc != '0' && nc != '#') {
2143 info.UseExponent = false;
2144 if (info.DecimalPointPos < 0)
2151 integerArea = false;
2153 exponentArea = false;
2154 if (info.DecimalPointPos == -1)
2155 info.DecimalPointPos = i;
2164 if (integerArea && info.IntegerDigits > 0)
2165 groupSeparatorCounter++;
2172 if (info.ExponentDigits == 0)
2173 info.UseExponent = false;
2175 info.IntegerHeadSharpDigits = 0;
2177 if (info.DecimalDigits == 0)
2178 info.DecimalPointPos = -1;
2180 info.DividePlaces += groupSeparatorCounter * 3;
2185 public string Format (string format, int offset, int length, NumberFormatInfo nfi, bool positive, StringBuilder sb_int, StringBuilder sb_dec, StringBuilder sb_exp)
2187 StringBuilder sb = new StringBuilder ();
2188 char literal = '\0';
2189 bool integerArea = true;
2190 bool decimalArea = false;
2191 int intSharpCounter = 0;
2192 int sb_int_index = 0;
2193 int sb_dec_index = 0;
2195 int[] groups = nfi.RawNumberGroupSizes;
2196 string groupSeparator = nfi.NumberGroupSeparator;
2197 int intLen = 0, total = 0, groupIndex = 0, counter = 0, groupSize = 0;
2198 if (UseGroup && groups.Length > 0) {
2199 intLen = sb_int.Length;
2200 for (int i = 0; i < groups.Length; i++) {
2201 total += groups [i];
2202 if (total <= intLen)
2205 groupSize = groups [groupIndex];
2206 int fraction = intLen > total ? intLen - total : 0;
2207 if (groupSize == 0) {
2208 while (groupIndex >= 0 && groups [groupIndex] == 0)
2211 groupSize = fraction > 0 ? fraction : groups [groupIndex];
2214 counter = groupSize;
2216 groupIndex += fraction / groupSize;
2217 counter = fraction % groupSize;
2219 counter = groupSize;
2227 for (int i = offset; i - offset < length; i++) {
2228 char c = format [i];
2230 if (c == literal && c != '\0') {
2234 if (literal != '\0') {
2242 if (i - offset < length)
2243 sb.Append (format [i]);
2247 if (c == '\"' || c == '\'')
2255 if (IntegerDigits - intSharpCounter < sb_int.Length + sb_int_index || c == '0')
2256 while (IntegerDigits - intSharpCounter + sb_int_index < sb_int.Length) {
2257 sb.Append (sb_int [sb_int_index++]);
2258 if (UseGroup && --intLen > 0 && --counter == 0) {
2259 sb.Append (groupSeparator);
2260 if (--groupIndex < groups.Length && groupIndex >= 0)
2261 groupSize = groups [groupIndex];
2262 counter = groupSize;
2267 else if (decimalArea) {
2268 if (sb_dec_index < sb_dec.Length)
2269 sb.Append (sb_dec [sb_dec_index++]);
2277 if (sb_exp == null || !UseExponent) {
2286 for (q = i + 1; q - offset < length; q++) {
2287 if (format [q] == '0') {
2291 if (q == i + 1 && (format [q] == '+' || format [q] == '-'))
2300 integerArea = (DecimalPointPos < 0);
2301 decimalArea = !integerArea;
2312 if (DecimalPointPos == i) {
2313 if (DecimalDigits > 0) {
2314 while (sb_int_index < sb_int.Length)
2315 sb.Append (sb_int [sb_int_index++]);
2317 if (sb_dec.Length > 0)
2318 sb.Append (nfi.NumberDecimalSeparator);
2320 integerArea = false;
2326 sb.Append (nfi.PercentSymbol);
2329 sb.Append (nfi.PerMilleSymbol);
2338 sb.Insert (0, nfi.NegativeSign);
2340 return sb.ToString ();