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 Int32DefPrecision = 10;
56 const int UInt32DefPrecision = 10;
57 const int Int64DefPrecision = 19;
58 const int UInt64DefPrecision = 20;
59 const int DecimalDefPrecision = 100;
60 const int TenPowersListLength = 19;
62 const double MinRoundtripVal = -1.79769313486231E+308;
63 const double MaxRoundtripVal = 1.79769313486231E+308;
65 // The below arrays are taken from mono/metatdata/number-formatter.h
67 private static readonly unsafe ulong* MantissaBitsTable;
68 private static readonly unsafe int* TensExponentTable;
69 private static readonly unsafe char* DigitLowerTable;
70 private static readonly unsafe char* DigitUpperTable;
71 private static readonly unsafe long* TenPowersList;
73 // DecHexDigits s a translation table from a decimal number to its
74 // digits hexadecimal representation (e.g. DecHexDigits [34] = 0x34).
75 private static readonly unsafe int* DecHexDigits;
77 [MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.InternalCall)]
78 private unsafe static extern void GetFormatterTables (out ulong* MantissaBitsTable, out int* TensExponentTable,
79 out char* DigitLowerTable, out char* DigitUpperTable,
80 out long* TenPowersList, out int* DecHexDigits);
82 unsafe static NumberFormatter()
84 GetFormatterTables (out MantissaBitsTable, out TensExponentTable,
85 out DigitLowerTable, out DigitUpperTable, out TenPowersList, out DecHexDigits);
88 unsafe static long GetTenPowerOf(int i)
90 return TenPowersList [i];
92 #endregion Static Fields
96 private NumberFormatInfo _nfi;
98 //part of the private stringbuffer
102 private bool _infinity;
103 private bool _isCustomFormat;
104 private bool _specifierIsUpper;
105 private bool _positive;
106 private char _specifier;
107 private int _precision;
108 private int _defPrecision;
110 private int _digitsLen;
111 private int _offset; // Represent the first digit offset.
112 private int _decPointPos;
114 // The following fields are a hexadeimal representation of the digits.
115 // For instance _val = 0x234 represents the digits '2', '3', '4'.
116 private uint _val1; // Digits 0 - 7.
117 private uint _val2; // Digits 8 - 15.
118 private uint _val3; // Digits 16 - 23.
119 private uint _val4; // Digits 23 - 31. Only needed for decimals.
123 #region Constructor Helpers
125 // Translate an unsigned int to hexadecimal digits.
126 // i.e. 123456789 is represented by _val1 = 0x23456789 and _val2 = 0x1
127 private void InitDecHexDigits (uint value)
129 if (value >= HundredMillion) {
130 int div1 = (int)(value / HundredMillion);
131 value -= HundredMillion * (uint)div1;
132 _val2 = FastToDecHex (div1);
134 _val1 = ToDecHex ((int)value);
137 // Translate an unsigned long to hexadecimal digits.
138 private void InitDecHexDigits (ulong value)
140 if (value >= HundredMillion) {
141 long div1 = (long)(value / HundredMillion);
142 value -= HundredMillion * (ulong)div1;
143 if (div1 >= HundredMillion) {
144 int div2 = (int)(div1 / HundredMillion);
145 div1 = div1 - div2 * (long)HundredMillion;
146 _val3 = ToDecHex (div2);
149 _val2 = ToDecHex ((int)(div1));
152 _val1 = ToDecHex ((int)value);
155 // Translate a decimal integer to hexadecimal digits.
156 // The decimal integer is 96 digits and its value is hi * 2^64 + lo.
157 // is the lower 64 bits.
158 private void InitDecHexDigits (uint hi, ulong lo)
161 InitDecHexDigits (lo); // Only the lower 64 bits matter.
165 // Compute (hi, lo) = (hi , lo) / HundredMillion.
166 uint divhi = hi / HundredMillion;
167 ulong remhi = hi - divhi * HundredMillion;
168 ulong divlo = lo / HundredMillion;
169 ulong remlo = lo - divlo * HundredMillion + remhi * ULongModHundredMillion;
171 lo = divlo + remhi * ULongDivHundredMillion;
172 divlo = remlo / HundredMillion;
173 remlo -= divlo * HundredMillion;
175 _val1 = ToDecHex ((int)remlo);
177 // Divide hi * 2 ^ 64 + lo by HundredMillion using the fact that
178 // hi < HundredMillion.
179 divlo = lo / HundredMillion;
180 remlo = lo - divlo * HundredMillion;
183 lo += hi * ULongDivHundredMillion;
184 remlo += hi * ULongModHundredMillion;
185 divlo = remlo / HundredMillion;
187 remlo -= divlo * HundredMillion;
189 _val2 = ToDecHex ((int)remlo);
191 // Now we are left with 64 bits store in lo.
192 if (lo >= HundredMillion) {
193 divlo = lo / HundredMillion;
194 lo -= divlo * HundredMillion;
195 _val4 = ToDecHex ((int)divlo);
197 _val3 = ToDecHex ((int)lo);
200 // Helper to translate an int in the range 0 .. 9999 to its
201 // Hexadecimal digits representation.
202 unsafe private static uint FastToDecHex (int val)
205 return (uint)DecHexDigits [val];
207 // Uses 2^19 (524288) to compute val / 100 for val < 10000.
208 int v = (val * 5243) >> 19;
209 return (uint)((DecHexDigits [v] << 8) | DecHexDigits [val - v * 100]);
212 // Helper to translate an int in the range 0 .. 99999999 to its
213 // Hexadecimal digits representation.
214 private static uint ToDecHex (int val)
220 res = FastToDecHex (v) << 16;
222 return res | FastToDecHex (val);
225 // Helper to count number of hexadecimal digits in a number.
226 private static int FastDecHexLen (int val)
233 else if (val < 0x1000)
239 private static int DecHexLen (uint val)
242 return FastDecHexLen ((int)val);
243 return 4 + FastDecHexLen ((int)(val >> 16));
246 // Count number of hexadecimal digits stored in _val1 .. _val4.
247 private int DecHexLen ()
250 return DecHexLen (_val4) + 24;
252 return DecHexLen (_val3) + 16;
254 return DecHexLen (_val2) + 8;
256 return DecHexLen (_val1);
261 // Helper to count the 10th scale (number of digits) in a number
262 private static int ScaleOrder (long hi)
264 for (int i = TenPowersListLength - 1; i >= 0; i--)
265 if (hi >= GetTenPowerOf (i))
270 // Compute the initial precision for rounding a floating number
271 // according to the used format.
272 int InitialFloatingPrecision ()
274 if (_specifier == 'R')
275 return _defPrecision + 2;
276 if (_precision < _defPrecision)
277 return _defPrecision;
278 if (_specifier == 'G')
279 return Math.Min (_defPrecision + 2, _precision);
280 if (_specifier == 'E')
281 return Math.Min (_defPrecision + 2, _precision + 1);
282 return _defPrecision;
285 // Parse the given format and extract the precision in it.
286 // Returns -1 for empty formats and -2 to indicate that the format
287 // is a custom format.
288 private static int ParsePrecision (string format)
291 for (int i = 1; i < format.Length; i++) {
292 int val = format [i] - '0';
293 precision = precision * 10 + val;
294 if (val < 0 || val > 9 || precision > 99)
300 #endregion Constructor Helpers
304 // Parse the given format and initialize the following fields:
305 // _isCustomFormat, _specifierIsUpper, _specifier & _precision.
306 NumberFormatter (Thread current)
308 _cbuf = EmptyArray<char>.Value;
311 CurrentCulture = current.CurrentCulture;
314 private void Init (string format)
316 _val1 = _val2 = _val3 = _val4 = 0;
318 _NaN = _infinity = false;
319 _isCustomFormat = false;
320 _specifierIsUpper = true;
323 if (format == null || format.Length == 0) {
328 char specifier = format [0];
329 if (specifier >= 'a' && specifier <= 'z') {
330 specifier = (char)(specifier - 'a' + 'A');
331 _specifierIsUpper = false;
333 else if (specifier < 'A' || specifier > 'Z') {
334 _isCustomFormat = true;
338 _specifier = specifier;
339 if (format.Length > 1) {
340 _precision = ParsePrecision (format);
341 if (_precision == -2) { // Is it a custom format?
342 _isCustomFormat = true;
349 private void InitHex (ulong value)
351 switch (_defPrecision) {
352 case Int32DefPrecision: value = (uint) value; break;
355 _val2 = (uint)(value >> 32);
356 _decPointPos = _digitsLen = DecHexLen ();
361 private void Init (string format, int value, int defPrecision)
364 _defPrecision = defPrecision;
365 _positive = value >= 0;
367 if (value == 0 || _specifier == 'X') {
368 InitHex ((ulong)value);
374 InitDecHexDigits ((uint)value);
375 _decPointPos = _digitsLen = DecHexLen ();
378 private void Init (string format, uint value, int defPrecision)
381 _defPrecision = defPrecision;
384 if (value == 0 || _specifier == 'X') {
389 InitDecHexDigits (value);
390 _decPointPos = _digitsLen = DecHexLen ();
393 private void Init (string format, long value)
396 _defPrecision = Int64DefPrecision;
397 _positive = value >= 0;
399 if (value == 0 || _specifier == 'X') {
400 InitHex ((ulong)value);
406 InitDecHexDigits ((ulong)value);
407 _decPointPos = _digitsLen = DecHexLen ();
410 private void Init (string format, ulong value)
413 _defPrecision = UInt64DefPrecision;
416 if (value == 0 || _specifier == 'X') {
417 InitHex ((ulong)value);
421 InitDecHexDigits (value);
422 _decPointPos = _digitsLen = DecHexLen ();
425 unsafe private void Init (string format, double value, int defPrecision)
429 _defPrecision = defPrecision;
430 long bits = BitConverter.DoubleToInt64Bits (value);
431 _positive = bits >= 0;
432 bits &= Int64.MaxValue;
440 int e = (int)(bits >> DoubleBitsExponentShift);
441 long m = bits & DoubleBitsMantissaMask;
442 if (e == DoubleBitsExponentMask) {
450 // We need 'm' to be large enough so we won't lose precision.
452 int scale = ScaleOrder (m);
453 if (scale < DoubleDefPrecision) {
454 expAdjust = scale - DoubleDefPrecision;
455 m *= GetTenPowerOf (-expAdjust);
459 m = (m + DoubleBitsMantissaMask + 1) * 10;
463 // multiply the mantissa by 10 ^ N
465 ulong hi = (ulong)m >> 32;
466 ulong lo2 = MantissaBitsTable [e];
467 ulong hi2 = lo2 >> 32;
469 ulong mm = hi * lo2 + lo * hi2 + ((lo * lo2) >> 32);
470 long res = (long)(hi * hi2 + (mm >> 32));
471 while (res < SeventeenDigitsThreshold) {
472 mm = (mm & UInt32.MaxValue) * 10;
473 res = res * 10 + (long)(mm >> 32);
476 if ((mm & 0x80000000) != 0)
479 int order = DoubleDefPrecision + 2;
480 _decPointPos = TensExponentTable [e] + expAdjust + order;
482 // Rescale 'res' to the initial precision (15-17 for doubles).
483 int initialPrecision = InitialFloatingPrecision ();
484 if (order > initialPrecision) {
485 long val = GetTenPowerOf (order - initialPrecision);
486 res = (res + (val >> 1)) / val;
487 order = initialPrecision;
489 if (res >= GetTenPowerOf (order)) {
494 InitDecHexDigits ((ulong)res);
495 _offset = CountTrailingZeros ();
496 _digitsLen = order - _offset;
499 private void Init (string format, decimal value)
502 _defPrecision = DecimalDefPrecision;
504 int[] bits = decimal.GetBits (value);
505 int scale = (bits [3] & DecimalBitsScaleMask) >> 16;
506 _positive = bits [3] >= 0;
507 if (bits [0] == 0 && bits [1] == 0 && bits [2] == 0) {
508 _decPointPos = -scale;
514 InitDecHexDigits ((uint)bits [2], ((ulong)bits [1] << 32) | (uint)bits [0]);
515 _digitsLen = DecHexLen ();
516 _decPointPos = _digitsLen - scale;
517 if (_precision != -1 || _specifier != 'G') {
518 _offset = CountTrailingZeros ();
519 _digitsLen -= _offset;
523 #endregion Constructors
525 #region Inner String Buffer
527 //_cbuf moved to before other fields to improve layout
530 private void ResetCharBuf (int size)
533 if (_cbuf.Length < size)
534 _cbuf = new char [size];
537 private void Resize (int len)
539 Array.Resize (ref _cbuf, len);
542 private void Append (char c)
544 if (_ind == _cbuf.Length)
549 private void Append (char c, int cnt)
551 if (_ind + cnt > _cbuf.Length)
552 Resize (_ind + cnt + 10);
557 private void Append (string s)
560 if (_ind + slen > _cbuf.Length)
561 Resize (_ind + slen + 10);
562 for (int i = 0; i < slen; i++)
563 _cbuf [_ind++] = s [i];
566 #endregion Inner String Buffer
568 #region Helper properties
570 private NumberFormatInfo GetNumberFormatInstance (IFormatProvider fp)
572 if (_nfi != null && fp == null)
574 return NumberFormatInfo.GetInstance (fp);
577 CultureInfo CurrentCulture {
579 if (value != null && value.IsReadOnly)
580 _nfi = value.NumberFormat;
586 private int IntegerDigits {
587 get { return _decPointPos > 0 ? _decPointPos : 1; }
590 private int DecimalDigits {
591 get { return _digitsLen > _decPointPos ? _digitsLen - _decPointPos : 0; }
594 private bool IsFloatingSource {
595 get { return _defPrecision == DoubleDefPrecision || _defPrecision == SingleDefPrecision; }
598 private bool IsZero {
599 get { return _digitsLen == 0; }
602 private bool IsZeroInteger {
603 get { return _digitsLen == 0 || _decPointPos <= 0; }
606 #endregion Helper properties
610 private void RoundPos (int pos)
612 RoundBits (_digitsLen - pos);
615 private bool RoundDecimal (int decimals)
617 return RoundBits (_digitsLen - _decPointPos - decimals);
620 private bool RoundBits (int shift)
625 if (shift > _digitsLen) {
628 _val1 = _val2 = _val3 = _val4 = 0;
633 _digitsLen += _offset;
642 shift = (shift - 1) << 2;
643 uint v = _val1 >> shift;
644 uint rem16 = v & 0xf;
645 _val1 = (v ^ rem16) << shift;
648 _val1 |= 0x99999999 >> (28 - shift);
650 int newlen = DecHexLen ();
651 res = newlen != _digitsLen;
652 _decPointPos = _decPointPos + newlen - _digitsLen;
655 RemoveTrailingZeros ();
659 private void RemoveTrailingZeros ()
661 _offset = CountTrailingZeros ();
662 _digitsLen -= _offset;
663 if (_digitsLen == 0) {
670 private void AddOneToDecHex ()
672 if (_val1 == 0x99999999) {
674 if (_val2 == 0x99999999) {
676 if (_val3 == 0x99999999) {
678 _val4 = AddOneToDecHex (_val4);
681 _val3 = AddOneToDecHex (_val3);
684 _val2 = AddOneToDecHex (_val2);
687 _val1 = AddOneToDecHex (_val1);
690 // Assume val != 0x99999999
691 private static uint AddOneToDecHex (uint val)
693 if ((val & 0xffff) == 0x9999)
694 if ((val & 0xffffff) == 0x999999)
695 if ((val & 0xfffffff) == 0x9999999)
696 return val + 0x06666667;
698 return val + 0x00666667;
699 else if ((val & 0xfffff) == 0x99999)
700 return val + 0x00066667;
702 return val + 0x00006667;
703 else if ((val & 0xff) == 0x99)
704 if ((val & 0xfff) == 0x999)
705 return val + 0x00000667;
707 return val + 0x00000067;
708 else if ((val & 0xf) == 0x9)
709 return val + 0x00000007;
714 private int CountTrailingZeros ()
717 return CountTrailingZeros (_val1);
719 return CountTrailingZeros (_val2) + 8;
721 return CountTrailingZeros (_val3) + 16;
723 return CountTrailingZeros (_val4) + 24;
727 private static int CountTrailingZeros (uint val)
729 if ((val & 0xffff) == 0)
730 if ((val & 0xffffff) == 0)
731 if ((val & 0xfffffff) == 0)
735 else if ((val & 0xfffff) == 0)
739 else if ((val & 0xff) == 0)
740 if ((val & 0xfff) == 0)
744 else if ((val & 0xf) == 0)
752 #region public number formatting methods
755 static NumberFormatter threadNumberFormatter;
758 static NumberFormatter userFormatProvider;
760 private static NumberFormatter GetInstance (IFormatProvider fp)
763 if (userFormatProvider == null) {
764 Interlocked.CompareExchange (ref userFormatProvider, new NumberFormatter (null), null);
767 return userFormatProvider;
770 NumberFormatter res = threadNumberFormatter;
771 threadNumberFormatter = null;
773 return new NumberFormatter (Thread.CurrentThread);
774 res.CurrentCulture = Thread.CurrentThread.CurrentCulture;
778 private void Release()
780 if (this != userFormatProvider)
781 threadNumberFormatter = this;
784 public static string NumberToString (string format, uint value, IFormatProvider fp)
786 NumberFormatter inst = GetInstance (fp);
787 inst.Init (format, value, Int32DefPrecision);
788 string res = inst.IntegerToString (format, fp);
793 public static string NumberToString (string format, int value, IFormatProvider fp)
795 NumberFormatter inst = GetInstance (fp);
796 inst.Init (format, value, UInt32DefPrecision);
797 string res = inst.IntegerToString (format, fp);
802 public static string NumberToString (string format, ulong value, IFormatProvider fp)
804 NumberFormatter inst = GetInstance (fp);
805 inst.Init (format, value);
806 string res = inst.IntegerToString (format, fp);
811 public static string NumberToString (string format, long value, IFormatProvider fp)
813 NumberFormatter inst = GetInstance (fp);
814 inst.Init (format, value);
815 string res = inst.IntegerToString (format, fp);
820 public static string NumberToString (string format, float value, IFormatProvider fp)
822 NumberFormatter inst = GetInstance (fp);
823 inst.Init (format, value, SingleDefPrecision);
824 NumberFormatInfo nfi = inst.GetNumberFormatInstance (fp);
828 else if (inst._infinity)
830 res = nfi.PositiveInfinitySymbol;
832 res = nfi.NegativeInfinitySymbol;
833 else if (inst._specifier == 'R')
834 res = inst.FormatRoundtrip (value, nfi);
836 res = inst.NumberToString (format, nfi);
841 public static string NumberToString (string format, double value, IFormatProvider fp)
843 NumberFormatter inst = GetInstance (fp);
844 inst.Init (format, value, DoubleDefPrecision);
845 NumberFormatInfo nfi = inst.GetNumberFormatInstance (fp);
849 else if (inst._infinity)
851 res = nfi.PositiveInfinitySymbol;
853 res = nfi.NegativeInfinitySymbol;
854 else if (inst._specifier == 'R')
855 res = inst.FormatRoundtrip (value, nfi);
857 res = inst.NumberToString (format, nfi);
862 public static string NumberToString (string format, decimal value, IFormatProvider fp)
864 NumberFormatter inst = GetInstance (fp);
865 inst.Init (format, value);
866 string res = inst.NumberToString (format, inst.GetNumberFormatInstance (fp));
871 private string IntegerToString (string format, IFormatProvider fp)
873 NumberFormatInfo nfi = GetNumberFormatInstance (fp);
874 switch (_specifier) {
876 return FormatCurrency (_precision, nfi);
878 return FormatDecimal (_precision, nfi);
880 return FormatExponential (_precision, nfi);
882 return FormatFixedPoint (_precision, nfi);
885 return FormatDecimal (-1, nfi);
886 return FormatGeneral (_precision, nfi);
888 return FormatNumber (_precision, nfi);
890 return FormatPercent (_precision, nfi);
892 return FormatHexadecimal (_precision);
895 return FormatCustom (format, nfi);
896 throw new FormatException ("The specified format '" + format + "' is invalid");
900 private string NumberToString (string format, NumberFormatInfo nfi)
902 switch (_specifier) {
904 return FormatCurrency (_precision, nfi);
906 return FormatExponential (_precision, nfi);
908 return FormatFixedPoint (_precision, nfi);
910 return FormatGeneral (_precision, nfi);
912 return FormatNumber (_precision, nfi);
914 return FormatPercent (_precision, nfi);
918 return FormatCustom (format, nfi);
919 throw new FormatException ("The specified format '" + format + "' is invalid");
923 string FormatCurrency (int precision, NumberFormatInfo nfi)
925 precision = (precision >= 0 ? precision : nfi.CurrencyDecimalDigits);
926 RoundDecimal (precision);
927 ResetCharBuf (IntegerDigits * 2 + precision * 2 + 16);
930 switch (nfi.CurrencyPositivePattern) {
932 Append (nfi.CurrencySymbol);
935 Append (nfi.CurrencySymbol);
941 switch (nfi.CurrencyNegativePattern) {
944 Append (nfi.CurrencySymbol);
947 Append (nfi.NegativeSign);
948 Append (nfi.CurrencySymbol);
951 Append (nfi.CurrencySymbol);
952 Append (nfi.NegativeSign);
955 Append (nfi.CurrencySymbol);
961 Append (nfi.NegativeSign);
964 Append (nfi.NegativeSign);
967 Append (nfi.NegativeSign);
968 Append (nfi.CurrencySymbol);
972 Append (nfi.CurrencySymbol);
976 Append (nfi.CurrencySymbol);
978 Append (nfi.NegativeSign);
982 Append (nfi.CurrencySymbol);
991 AppendIntegerStringWithGroupSeparator (nfi.CurrencyGroupSizes, nfi.CurrencyGroupSeparator);
994 Append (nfi.CurrencyDecimalSeparator);
995 AppendDecimalString (precision);
999 switch (nfi.CurrencyPositivePattern) {
1001 Append (nfi.CurrencySymbol);
1005 Append (nfi.CurrencySymbol);
1010 switch (nfi.CurrencyNegativePattern) {
1015 Append (nfi.NegativeSign);
1018 Append (nfi.CurrencySymbol);
1022 Append (nfi.CurrencySymbol);
1025 Append (nfi.NegativeSign);
1026 Append (nfi.CurrencySymbol);
1029 Append (nfi.CurrencySymbol);
1030 Append (nfi.NegativeSign);
1034 Append (nfi.CurrencySymbol);
1038 Append (nfi.CurrencySymbol);
1039 Append (nfi.NegativeSign);
1042 Append (nfi.NegativeSign);
1045 Append (nfi.NegativeSign);
1047 Append (nfi.CurrencySymbol);
1054 Append (nfi.CurrencySymbol);
1060 return new string (_cbuf, 0, _ind);
1063 private string FormatDecimal (int precision, NumberFormatInfo nfi)
1065 if (precision < _digitsLen)
1066 precision = _digitsLen;
1070 ResetCharBuf (precision + 1);
1072 Append (nfi.NegativeSign);
1073 AppendDigits (0, precision);
1075 return new string (_cbuf, 0, _ind);
1078 unsafe private string FormatHexadecimal (int precision)
1080 int size = Math.Max (precision, _decPointPos);
1081 char* digits = _specifierIsUpper ? DigitUpperTable : DigitLowerTable;
1083 ResetCharBuf (size);
1085 ulong val = _val1 | ((ulong)_val2 << 32);
1087 _cbuf [--size] = digits [val & 0xf];
1090 return new string (_cbuf, 0, _ind);
1093 string FormatFixedPoint (int precision, NumberFormatInfo nfi)
1095 if (precision == -1)
1096 precision = nfi.NumberDecimalDigits;
1098 RoundDecimal (precision);
1100 ResetCharBuf (IntegerDigits + precision + 2);
1103 Append (nfi.NegativeSign);
1105 AppendIntegerString (IntegerDigits);
1107 if (precision > 0) {
1108 Append (nfi.NumberDecimalSeparator);
1109 AppendDecimalString (precision);
1112 return new string (_cbuf, 0, _ind);
1115 private string FormatRoundtrip (double origval, NumberFormatInfo nfi)
1117 NumberFormatter nfc = GetClone ();
1118 if (origval >= MinRoundtripVal && origval <= MaxRoundtripVal) {
1119 string shortRep = FormatGeneral (_defPrecision, nfi);
1120 if (origval == Double.Parse (shortRep, nfi))
1123 return nfc.FormatGeneral (_defPrecision + 2, nfi);
1126 private string FormatRoundtrip (float origval, NumberFormatInfo nfi)
1128 NumberFormatter nfc = GetClone ();
1129 string shortRep = FormatGeneral (_defPrecision, nfi);
1130 // Check roundtrip only for "normal" double values.
1131 if (origval == Single.Parse (shortRep, nfi))
1133 return nfc.FormatGeneral (_defPrecision + 2, nfi);
1136 private string FormatGeneral (int precision, NumberFormatInfo nfi)
1139 if (precision == -1) {
1140 enableExp = IsFloatingSource;
1141 precision = _defPrecision;
1146 precision = _defPrecision;
1147 RoundPos (precision);
1150 int intDigits = _decPointPos;
1151 int digits = _digitsLen;
1152 int decDigits = digits - intDigits;
1154 if ((intDigits > precision || intDigits <= -4) && enableExp)
1155 return FormatExponential (digits - 1, nfi, 2);
1161 ResetCharBuf (decDigits + intDigits + 3);
1164 Append (nfi.NegativeSign);
1169 AppendDigits (digits - intDigits, digits);
1171 if (decDigits > 0) {
1172 Append (nfi.NumberDecimalSeparator);
1173 AppendDigits (0, decDigits);
1176 return new string (_cbuf, 0, _ind);
1179 string FormatNumber (int precision, NumberFormatInfo nfi)
1181 precision = (precision >= 0 ? precision : nfi.NumberDecimalDigits);
1182 ResetCharBuf (IntegerDigits * 3 + precision);
1183 RoundDecimal (precision);
1186 switch (nfi.NumberNegativePattern) {
1191 Append (nfi.NegativeSign);
1194 Append (nfi.NegativeSign);
1200 AppendIntegerStringWithGroupSeparator (nfi.NumberGroupSizes, nfi.NumberGroupSeparator);
1202 if (precision > 0) {
1203 Append (nfi.NumberDecimalSeparator);
1204 AppendDecimalString (precision);
1208 switch (nfi.NumberNegativePattern) {
1213 Append (nfi.NegativeSign);
1217 Append (nfi.NegativeSign);
1222 return new string (_cbuf, 0, _ind);
1225 string FormatPercent (int precision, NumberFormatInfo nfi)
1227 precision = (precision >= 0 ? precision : nfi.PercentDecimalDigits);
1229 RoundDecimal (precision);
1230 ResetCharBuf (IntegerDigits * 2 + precision + 16);
1233 if (nfi.PercentPositivePattern == 2)
1234 Append (nfi.PercentSymbol);
1237 switch (nfi.PercentNegativePattern) {
1239 Append (nfi.NegativeSign);
1242 Append (nfi.NegativeSign);
1245 Append (nfi.NegativeSign);
1246 Append (nfi.PercentSymbol);
1251 AppendIntegerStringWithGroupSeparator (nfi.PercentGroupSizes, nfi.PercentGroupSeparator);
1253 if (precision > 0) {
1254 Append (nfi.PercentDecimalSeparator);
1255 AppendDecimalString (precision);
1259 switch (nfi.PercentPositivePattern) {
1262 Append (nfi.PercentSymbol);
1265 Append (nfi.PercentSymbol);
1270 switch (nfi.PercentNegativePattern) {
1273 Append (nfi.PercentSymbol);
1276 Append (nfi.PercentSymbol);
1281 return new string (_cbuf, 0, _ind);
1284 string FormatExponential (int precision, NumberFormatInfo nfi)
1286 if (precision == -1)
1287 precision = DefaultExpPrecision;
1289 RoundPos (precision + 1);
1290 return FormatExponential (precision, nfi, 3);
1293 private string FormatExponential (int precision, NumberFormatInfo nfi, int expDigits)
1295 int decDigits = _decPointPos;
1296 int digits = _digitsLen;
1297 int exponent = decDigits - 1;
1298 decDigits = _decPointPos = 1;
1300 ResetCharBuf (precision + 8);
1303 Append (nfi.NegativeSign);
1305 AppendOneDigit (digits - 1);
1307 if (precision > 0) {
1308 Append (nfi.NumberDecimalSeparator);
1309 AppendDigits (digits - precision - 1, digits - _decPointPos);
1312 AppendExponent (nfi, exponent, expDigits);
1314 return new string (_cbuf, 0, _ind);
1317 string FormatCustom (string format, NumberFormatInfo nfi)
1322 CustomInfo.GetActiveSection (format, ref p, IsZero, ref offset, ref length);
1324 return _positive ? string.Empty : nfi.NegativeSign;
1327 CustomInfo info = CustomInfo.Parse (format, offset, length, nfi);
1329 Console.WriteLine ("Format : {0}",format);
1330 Console.WriteLine ("DecimalDigits : {0}",info.DecimalDigits);
1331 Console.WriteLine ("DecimalPointPos : {0}",info.DecimalPointPos);
1332 Console.WriteLine ("DecimalTailSharpDigits : {0}",info.DecimalTailSharpDigits);
1333 Console.WriteLine ("IntegerDigits : {0}",info.IntegerDigits);
1334 Console.WriteLine ("IntegerHeadSharpDigits : {0}",info.IntegerHeadSharpDigits);
1335 Console.WriteLine ("IntegerHeadPos : {0}",info.IntegerHeadPos);
1336 Console.WriteLine ("UseExponent : {0}",info.UseExponent);
1337 Console.WriteLine ("ExponentDigits : {0}",info.ExponentDigits);
1338 Console.WriteLine ("ExponentTailSharpDigits : {0}",info.ExponentTailSharpDigits);
1339 Console.WriteLine ("ExponentNegativeSignOnly : {0}",info.ExponentNegativeSignOnly);
1340 Console.WriteLine ("DividePlaces : {0}",info.DividePlaces);
1341 Console.WriteLine ("Percents : {0}",info.Percents);
1342 Console.WriteLine ("Permilles : {0}",info.Permilles);
1344 StringBuilder sb_int = new StringBuilder (info.IntegerDigits * 2);
1345 StringBuilder sb_dec = new StringBuilder (info.DecimalDigits * 2);
1346 StringBuilder sb_exp = (info.UseExponent ? new StringBuilder (info.ExponentDigits * 2) : null);
1349 if (info.Percents > 0)
1350 Multiply10(2 * info.Percents);
1351 if (info.Permilles > 0)
1352 Multiply10(3 * info.Permilles);
1353 if (info.DividePlaces > 0)
1354 Divide10(info.DividePlaces);
1356 bool expPositive = true;
1357 if (info.UseExponent && (info.DecimalDigits > 0 || info.IntegerDigits > 0)) {
1359 RoundPos (info.DecimalDigits + info.IntegerDigits);
1360 diff -= _decPointPos - info.IntegerDigits;
1361 _decPointPos = info.IntegerDigits;
1364 expPositive = diff <= 0;
1365 AppendNonNegativeNumber (sb_exp, diff < 0 ? -diff : diff);
1368 RoundDecimal (info.DecimalDigits);
1370 if (info.IntegerDigits != 0 || !IsZeroInteger)
1371 AppendIntegerString (IntegerDigits, sb_int);
1373 AppendDecimalString (DecimalDigits, sb_dec);
1375 if (info.UseExponent) {
1376 if (info.DecimalDigits <= 0 && info.IntegerDigits <= 0)
1379 if (sb_int.Length < info.IntegerDigits)
1380 sb_int.Insert (0, "0", info.IntegerDigits - sb_int.Length);
1382 while (sb_exp.Length < info.ExponentDigits - info.ExponentTailSharpDigits)
1383 sb_exp.Insert (0, '0');
1385 if (expPositive && !info.ExponentNegativeSignOnly)
1386 sb_exp.Insert (0, nfi.PositiveSign);
1387 else if (!expPositive)
1388 sb_exp.Insert (0, nfi.NegativeSign);
1391 if (sb_int.Length < info.IntegerDigits - info.IntegerHeadSharpDigits)
1392 sb_int.Insert (0, "0", info.IntegerDigits - info.IntegerHeadSharpDigits - sb_int.Length);
1393 if (info.IntegerDigits == info.IntegerHeadSharpDigits && IsZeroOnly (sb_int))
1394 sb_int.Remove (0, sb_int.Length);
1397 ZeroTrimEnd (sb_dec, true);
1398 while (sb_dec.Length < info.DecimalDigits - info.DecimalTailSharpDigits)
1399 sb_dec.Append ('0');
1400 if (sb_dec.Length > info.DecimalDigits)
1401 sb_dec.Remove (info.DecimalDigits, sb_dec.Length - info.DecimalDigits);
1403 return info.Format (format, offset, length, nfi, _positive, sb_int, sb_dec, sb_exp);
1405 #endregion public number formatting methods
1407 #region StringBuilder formatting helpers
1409 private static void ZeroTrimEnd (StringBuilder sb, bool canEmpty)
1412 for (int i = sb.Length - 1; (canEmpty ? i >= 0 : i > 0); i--) {
1419 sb.Remove (sb.Length - len, len);
1422 private static bool IsZeroOnly (StringBuilder sb)
1424 for (int i = 0; i < sb.Length; i++)
1425 if (char.IsDigit (sb [i]) && sb [i] != '0')
1430 private static void AppendNonNegativeNumber (StringBuilder sb, int v)
1433 throw new ArgumentException ();
1435 int i = ScaleOrder (v) - 1;
1437 int n = v / (int)GetTenPowerOf (i);
1438 sb.Append ((char)('0' | n));
1439 v -= (int)GetTenPowerOf (i--) * n;
1443 #endregion StringBuilder formatting helpers
1445 #region Append helpers
1447 private void AppendIntegerString (int minLength, StringBuilder sb)
1449 if (_decPointPos <= 0) {
1450 sb.Append ('0', minLength);
1454 if (_decPointPos < minLength)
1455 sb.Append ('0', minLength - _decPointPos);
1457 AppendDigits (_digitsLen - _decPointPos, _digitsLen, sb);
1460 private void AppendIntegerString (int minLength)
1462 if (_decPointPos <= 0) {
1463 Append ('0', minLength);
1467 if (_decPointPos < minLength)
1468 Append ('0', minLength - _decPointPos);
1470 AppendDigits (_digitsLen - _decPointPos, _digitsLen);
1473 private void AppendDecimalString (int precision, StringBuilder sb)
1475 AppendDigits (_digitsLen - precision - _decPointPos, _digitsLen - _decPointPos, sb);
1478 private void AppendDecimalString (int precision)
1480 AppendDigits (_digitsLen - precision - _decPointPos, _digitsLen - _decPointPos);
1483 private void AppendIntegerStringWithGroupSeparator (int[] groups, string groupSeparator)
1485 if (IsZeroInteger) {
1492 for (int i = 0; i < groups.Length; i++) {
1493 total += groups [i];
1494 if (total <= _decPointPos)
1500 if (groups.Length > 0 && total > 0) {
1502 int groupSize = groups [groupIndex];
1503 int fraction = _decPointPos > total ? _decPointPos - total : 0;
1504 if (groupSize == 0) {
1505 while (groupIndex >= 0 && groups [groupIndex] == 0)
1508 groupSize = fraction > 0 ? fraction : groups [groupIndex];
1511 counter = groupSize;
1513 groupIndex += fraction / groupSize;
1514 counter = fraction % groupSize;
1516 counter = groupSize;
1521 if (total >= _decPointPos) {
1522 int lastGroupSize = groups [0];
1523 if (total > lastGroupSize) {
1524 int lastGroupDiff = -(lastGroupSize - _decPointPos);
1527 if (lastGroupDiff < lastGroupSize)
1528 counter = lastGroupDiff;
1529 else if (lastGroupSize > 0 && (lastGroupMod = _decPointPos % lastGroupSize) > 0)
1530 counter = lastGroupMod;
1534 for (int i = 0; ;) {
1535 if ((_decPointPos - i) <= counter || counter == 0) {
1536 AppendDigits (_digitsLen - _decPointPos, _digitsLen - i);
1539 AppendDigits (_digitsLen - i - counter, _digitsLen - i);
1541 Append (groupSeparator);
1542 if (--groupIndex < groups.Length && groupIndex >= 0)
1543 groupSize = groups [groupIndex];
1544 counter = groupSize;
1548 AppendDigits (_digitsLen - _decPointPos, _digitsLen);
1552 // minDigits is in the range 1..3
1553 private void AppendExponent (NumberFormatInfo nfi, int exponent, int minDigits)
1555 if (_specifierIsUpper || _specifier == 'R')
1561 Append (nfi.PositiveSign);
1563 Append (nfi.NegativeSign);
1564 exponent = -exponent;
1568 Append ('0', minDigits);
1569 else if (exponent < 10) {
1570 Append ('0', minDigits - 1);
1571 Append ((char)('0' | exponent));
1574 uint hexDigit = FastToDecHex (exponent);
1575 if (exponent >= 100 || minDigits == 3)
1576 Append ((char)('0' | (hexDigit >> 8)));
1577 Append ((char)('0' | ((hexDigit >> 4) & 0xf)));
1578 Append ((char)('0' | (hexDigit & 0xf)));
1582 private void AppendOneDigit (int start)
1584 if (_ind == _cbuf.Length)
1593 else if (start < 16)
1595 else if (start < 24)
1597 else if (start < 32)
1601 v >>= (start & 0x7) << 2;
1602 _cbuf [_ind++] = (char)('0' | v & 0xf);
1605 private void AppendDigits (int start, int end)
1610 int i = _ind + (end - start);
1611 if (i > _cbuf.Length)
1618 for (int next = start + 8 - (start & 0x7); ; start = next, next += 8) {
1622 else if (next == 16)
1624 else if (next == 24)
1626 else if (next == 32)
1630 v >>= (start & 0x7) << 2;
1634 _cbuf [--i] = (char)('0' | v & 0xf);
1635 switch (next - start) {
1637 _cbuf [--i] = (char)('0' | (v >>= 4) & 0xf);
1640 _cbuf [--i] = (char)('0' | (v >>= 4) & 0xf);
1643 _cbuf [--i] = (char)('0' | (v >>= 4) & 0xf);
1646 _cbuf [--i] = (char)('0' | (v >>= 4) & 0xf);
1649 _cbuf [--i] = (char)('0' | (v >>= 4) & 0xf);
1652 _cbuf [--i] = (char)('0' | (v >>= 4) & 0xf);
1655 _cbuf [--i] = (char)('0' | (v >>= 4) & 0xf);
1665 private void AppendDigits (int start, int end, StringBuilder sb)
1670 int i = sb.Length + (end - start);
1676 for (int next = start + 8 - (start & 0x7); ; start = next, next += 8) {
1680 else if (next == 16)
1682 else if (next == 24)
1684 else if (next == 32)
1688 v >>= (start & 0x7) << 2;
1691 sb [--i] = (char)('0' | v & 0xf);
1692 switch (next - start) {
1694 sb [--i] = (char)('0' | (v >>= 4) & 0xf);
1697 sb [--i] = (char)('0' | (v >>= 4) & 0xf);
1700 sb [--i] = (char)('0' | (v >>= 4) & 0xf);
1703 sb [--i] = (char)('0' | (v >>= 4) & 0xf);
1706 sb [--i] = (char)('0' | (v >>= 4) & 0xf);
1709 sb [--i] = (char)('0' | (v >>= 4) & 0xf);
1712 sb [--i] = (char)('0' | (v >>= 4) & 0xf);
1722 #endregion Append helpers
1726 private void Multiply10(int count)
1728 if (count <= 0 || _digitsLen == 0)
1731 _decPointPos += count;
1734 private void Divide10(int count)
1736 if (count <= 0 || _digitsLen == 0)
1739 _decPointPos -= count;
1742 private NumberFormatter GetClone ()
1744 return (NumberFormatter)this.MemberwiseClone ();
1751 private class CustomInfo
1753 public bool UseGroup = false;
1754 public int DecimalDigits = 0;
1755 public int DecimalPointPos = -1;
1756 public int DecimalTailSharpDigits = 0;
1757 public int IntegerDigits = 0;
1758 public int IntegerHeadSharpDigits = 0;
1759 public int IntegerHeadPos = 0;
1760 public bool UseExponent = false;
1761 public int ExponentDigits = 0;
1762 public int ExponentTailSharpDigits = 0;
1763 public bool ExponentNegativeSignOnly = true;
1764 public int DividePlaces = 0;
1765 public int Percents = 0;
1766 public int Permilles = 0;
1768 public static void GetActiveSection (string format, ref bool positive, bool zero, ref int offset, ref int length)
1770 int[] lens = new int [3];
1773 bool quoted = false;
1775 for (int i = 0; i < format.Length; i++) {
1776 char c = format [i];
1778 if (c == '\"' || c == '\'') {
1779 if (i == 0 || format [i - 1] != '\\')
1785 if (c == ';' && !quoted && (i == 0 || format [i - 1] != '\\')) {
1786 lens [index++] = i - lastPos;
1795 length = format.Length;
1799 if (positive || zero) {
1804 if (lens [0] + 1 < format.Length) {
1806 offset = lens [0] + 1;
1807 length = format.Length - offset;
1818 offset = lens [0] + lens [1] + 2;
1819 length = format.Length - offset;
1829 offset = lens [0] + 1;
1841 offset = lens [0] + lens [1] + 2;
1852 offset = lens [0] + 1;
1863 throw new ArgumentException ();
1866 public static CustomInfo Parse (string format, int offset, int length, NumberFormatInfo nfi)
1868 char literal = '\0';
1869 bool integerArea = true;
1870 bool decimalArea = false;
1871 bool exponentArea = false;
1872 bool sharpContinues = true;
1874 CustomInfo info = new CustomInfo ();
1875 int groupSeparatorCounter = 0;
1877 for (int i = offset; i - offset < length; i++) {
1878 char c = format [i];
1880 if (c == literal && c != '\0') {
1884 if (literal != '\0')
1887 if (exponentArea && (c != '\0' && c != '0' && c != '#')) {
1888 exponentArea = false;
1889 integerArea = (info.DecimalPointPos < 0);
1890 decimalArea = !integerArea;
1901 if (c == '\"' || c == '\'') {
1906 if (sharpContinues && integerArea)
1907 info.IntegerHeadSharpDigits++;
1908 else if (decimalArea)
1909 info.DecimalTailSharpDigits++;
1910 else if (exponentArea)
1911 info.ExponentTailSharpDigits++;
1916 sharpContinues = false;
1918 info.DecimalTailSharpDigits = 0;
1919 else if (exponentArea)
1920 info.ExponentTailSharpDigits = 0;
1922 if (info.IntegerHeadPos == -1)
1923 info.IntegerHeadPos = i;
1926 info.IntegerDigits++;
1927 if (groupSeparatorCounter > 0)
1928 info.UseGroup = true;
1929 groupSeparatorCounter = 0;
1931 else if (decimalArea)
1932 info.DecimalDigits++;
1933 else if (exponentArea)
1934 info.ExponentDigits++;
1938 if (info.UseExponent)
1941 info.UseExponent = true;
1942 integerArea = false;
1943 decimalArea = false;
1944 exponentArea = true;
1945 if (i + 1 - offset < length) {
1946 char nc = format [i + 1];
1948 info.ExponentNegativeSignOnly = false;
1949 if (nc == '+' || nc == '-')
1951 else if (nc != '0' && nc != '#') {
1952 info.UseExponent = false;
1953 if (info.DecimalPointPos < 0)
1960 integerArea = false;
1962 exponentArea = false;
1963 if (info.DecimalPointPos == -1)
1964 info.DecimalPointPos = i;
1973 if (integerArea && info.IntegerDigits > 0)
1974 groupSeparatorCounter++;
1981 if (info.ExponentDigits == 0)
1982 info.UseExponent = false;
1984 info.IntegerHeadSharpDigits = 0;
1986 if (info.DecimalDigits == 0)
1987 info.DecimalPointPos = -1;
1989 info.DividePlaces += groupSeparatorCounter * 3;
1994 public string Format (string format, int offset, int length, NumberFormatInfo nfi, bool positive, StringBuilder sb_int, StringBuilder sb_dec, StringBuilder sb_exp)
1996 StringBuilder sb = new StringBuilder ();
1997 char literal = '\0';
1998 bool integerArea = true;
1999 bool decimalArea = false;
2000 int intSharpCounter = 0;
2001 int sb_int_index = 0;
2002 int sb_dec_index = 0;
2004 int[] groups = nfi.NumberGroupSizes;
2005 string groupSeparator = nfi.NumberGroupSeparator;
2006 int intLen = 0, total = 0, groupIndex = 0, counter = 0, groupSize = 0;
2007 if (UseGroup && groups.Length > 0) {
2008 intLen = sb_int.Length;
2009 for (int i = 0; i < groups.Length; i++) {
2010 total += groups [i];
2011 if (total <= intLen)
2014 groupSize = groups [groupIndex];
2015 int fraction = intLen > total ? intLen - total : 0;
2016 if (groupSize == 0) {
2017 while (groupIndex >= 0 && groups [groupIndex] == 0)
2020 groupSize = fraction > 0 ? fraction : groups [groupIndex];
2023 counter = groupSize;
2025 groupIndex += fraction / groupSize;
2026 counter = fraction % groupSize;
2028 counter = groupSize;
2036 for (int i = offset; i - offset < length; i++) {
2037 char c = format [i];
2039 if (c == literal && c != '\0') {
2043 if (literal != '\0') {
2051 if (i - offset < length)
2052 sb.Append (format [i]);
2056 if (c == '\"' || c == '\'')
2064 if (IntegerDigits - intSharpCounter < sb_int.Length + sb_int_index || c == '0')
2065 while (IntegerDigits - intSharpCounter + sb_int_index < sb_int.Length) {
2066 sb.Append (sb_int [sb_int_index++]);
2067 if (UseGroup && --intLen > 0 && --counter == 0) {
2068 sb.Append (groupSeparator);
2069 if (--groupIndex < groups.Length && groupIndex >= 0)
2070 groupSize = groups [groupIndex];
2071 counter = groupSize;
2076 else if (decimalArea) {
2077 if (sb_dec_index < sb_dec.Length)
2078 sb.Append (sb_dec [sb_dec_index++]);
2086 if (sb_exp == null || !UseExponent) {
2095 for (q = i + 1; q - offset < length; q++) {
2096 if (format [q] == '0') {
2100 if (q == i + 1 && (format [q] == '+' || format [q] == '-'))
2109 integerArea = (DecimalPointPos < 0);
2110 decimalArea = !integerArea;
2121 if (DecimalPointPos == i) {
2122 if (DecimalDigits > 0) {
2123 while (sb_int_index < sb_int.Length)
2124 sb.Append (sb_int [sb_int_index++]);
2126 if (sb_dec.Length > 0)
2127 sb.Append (nfi.NumberDecimalSeparator);
2129 integerArea = false;
2135 sb.Append (nfi.PercentSymbol);
2138 sb.Append (nfi.PerMilleSymbol);
2147 sb.Insert (0, nfi.NegativeSign);
2149 return sb.ToString ();