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
;
112 private bool _infinity
;
113 private bool _isCustomFormat
;
114 private bool _specifierIsUpper
;
115 private bool _positive
;
116 private char _specifier
;
117 private int _precision
;
118 private int _defPrecision
;
120 private int _digitsLen
;
121 private int _offset
; // Represent the first digit offset.
122 private int _decPointPos
;
124 // The following fields are a hexadeimal representation of the digits.
125 // For instance _val = 0x234 represents the digits '2', '3', '4'.
126 private uint _val1
; // Digits 0 - 7.
127 private uint _val2
; // Digits 8 - 15.
128 private uint _val3
; // Digits 16 - 23.
129 private uint _val4
; // Digits 23 - 31. Only needed for decimals.
133 #region Constructor Helpers
135 // Translate an unsigned int to hexadecimal digits.
136 // i.e. 123456789 is represented by _val1 = 0x23456789 and _val2 = 0x1
137 private void InitDecHexDigits (uint value)
139 if (value >= HundredMillion
) {
140 int div1
= (int)(value / HundredMillion
);
141 value -= HundredMillion
* (uint)div1
;
142 _val2
= FastToDecHex (div1
);
144 _val1
= ToDecHex ((int)value);
147 // Translate an unsigned long to hexadecimal digits.
148 private void InitDecHexDigits (ulong value)
150 if (value >= HundredMillion
) {
151 long div1
= (long)(value / HundredMillion
);
152 value -= HundredMillion
* (ulong)div1
;
153 if (div1
>= HundredMillion
) {
154 int div2
= (int)(div1
/ HundredMillion
);
155 div1
= div1
- div2
* (long)HundredMillion
;
156 _val3
= ToDecHex (div2
);
159 _val2
= ToDecHex ((int)(div1
));
162 _val1
= ToDecHex ((int)value);
165 // Translate a decimal integer to hexadecimal digits.
166 // The decimal integer is 96 digits and its value is hi * 2^64 + lo.
167 // is the lower 64 bits.
168 private void InitDecHexDigits (uint hi
, ulong lo
)
171 InitDecHexDigits (lo
); // Only the lower 64 bits matter.
175 // Compute (hi, lo) = (hi , lo) / HundredMillion.
176 uint divhi
= hi
/ HundredMillion
;
177 ulong remhi
= hi
- divhi
* HundredMillion
;
178 ulong divlo
= lo
/ HundredMillion
;
179 ulong remlo
= lo
- divlo
* HundredMillion
+ remhi
* ULongModHundredMillion
;
181 lo
= divlo
+ remhi
* ULongDivHundredMillion
;
182 divlo
= remlo
/ HundredMillion
;
183 remlo
-= divlo
* HundredMillion
;
185 _val1
= ToDecHex ((int)remlo
);
187 // Divide hi * 2 ^ 64 + lo by HundredMillion using the fact that
188 // hi < HundredMillion.
189 divlo
= lo
/ HundredMillion
;
190 remlo
= lo
- divlo
* HundredMillion
;
193 lo
+= hi
* ULongDivHundredMillion
;
194 remlo
+= hi
* ULongModHundredMillion
;
195 divlo
= remlo
/ HundredMillion
;
197 remlo
-= divlo
* HundredMillion
;
199 _val2
= ToDecHex ((int)remlo
);
201 // Now we are left with 64 bits store in lo.
202 if (lo
>= HundredMillion
) {
203 divlo
= lo
/ HundredMillion
;
204 lo
-= divlo
* HundredMillion
;
205 _val4
= ToDecHex ((int)divlo
);
207 _val3
= ToDecHex ((int)lo
);
210 // Helper to translate an int in the range 0 .. 9999 to its
211 // Hexadecimal digits representation.
215 private static uint FastToDecHex (int val
)
218 return (uint)DecHexDigits
[val
];
220 // Uses 2^19 (524288) to compute val / 100 for val < 10000.
221 int v
= (val
* 5243) >> 19;
222 return (uint)((DecHexDigits
[v
] << 8) | DecHexDigits
[val
- v
* 100]);
225 // Helper to translate an int in the range 0 .. 99999999 to its
226 // Hexadecimal digits representation.
227 private static uint ToDecHex (int val
)
233 res
= FastToDecHex (v
) << 16;
235 return res
| FastToDecHex (val
);
238 // Helper to count number of hexadecimal digits in a number.
239 private static int FastDecHexLen (int val
)
246 else if (val
< 0x1000)
252 private static int DecHexLen (uint val
)
255 return FastDecHexLen ((int)val
);
256 return 4 + FastDecHexLen ((int)(val
>> 16));
259 // Count number of hexadecimal digits stored in _val1 .. _val4.
260 private int DecHexLen ()
263 return DecHexLen (_val4
) + 24;
265 return DecHexLen (_val3
) + 16;
267 return DecHexLen (_val2
) + 8;
269 return DecHexLen (_val1
);
274 // Helper to count the 10th scale (number of digits) in a number
275 private static int ScaleOrder (long hi
)
277 for (int i
= TenPowersListLength
- 1; i
>= 0; i
--)
278 if (hi
>= GetTenPowerOf (i
))
283 // Compute the initial precision for rounding a floating number
284 // according to the used format.
285 int InitialFloatingPrecision ()
287 if (_specifier
== 'R')
288 return _defPrecision
+ 2;
289 if (_precision
< _defPrecision
)
290 return _defPrecision
;
291 if (_specifier
== 'G')
292 return Math
.Min (_defPrecision
+ 2, _precision
);
293 if (_specifier
== 'E')
294 return Math
.Min (_defPrecision
+ 2, _precision
+ 1);
295 return _defPrecision
;
298 // Parse the given format and extract the precision in it.
299 // Returns -1 for empty formats and -2 to indicate that the format
300 // is a custom format.
301 private static int ParsePrecision (string format
)
304 for (int i
= 1; i
< format
.Length
; i
++) {
305 int val
= format
[i
] - '0';
306 precision
= precision
* 10 + val
;
307 if (val
< 0 || val
> 9 || precision
> 99)
313 #endregion Constructor Helpers
317 // Parse the given format and initialize the following fields:
318 // _isCustomFormat, _specifierIsUpper, _specifier & _precision.
319 public NumberFormatter (Thread current
)
321 _cbuf
= new char [0];
324 CurrentCulture
= current
.CurrentCulture
;
327 private void Init (string format
)
329 _val1
= _val2
= _val3
= _val4
= 0;
331 _NaN
= _infinity
= false;
332 _isCustomFormat
= false;
333 _specifierIsUpper
= true;
336 if (format
== null || format
.Length
== 0) {
341 char specifier
= format
[0];
342 if (specifier
>= 'a' && specifier
<= 'z') {
343 specifier
= (char)(specifier
- 'a' + 'A');
344 _specifierIsUpper
= false;
346 else if (specifier
< 'A' || specifier
> 'Z') {
347 _isCustomFormat
= true;
351 _specifier
= specifier
;
352 if (format
.Length
> 1) {
353 _precision
= ParsePrecision (format
);
354 if (_precision
== -2) { // Is it a custom format?
355 _isCustomFormat
= true;
362 private void InitHex (ulong value)
364 switch (_defPrecision
) {
365 case Int8DefPrecision
: value = (byte) value; break;
366 case Int16DefPrecision
: value = (ushort) value; break;
367 case Int32DefPrecision
: value = (uint) value; break;
370 _val2
= (uint)(value >> 32);
371 _decPointPos
= _digitsLen
= DecHexLen ();
376 private void Init (string format
, int value, int defPrecision
)
379 _defPrecision
= defPrecision
;
380 _positive
= value >= 0;
382 if (value == 0 || _specifier
== 'X') {
383 InitHex ((ulong)value);
389 InitDecHexDigits ((uint)value);
390 _decPointPos
= _digitsLen
= DecHexLen ();
393 private void Init (string format
, uint value, int defPrecision
)
396 _defPrecision
= defPrecision
;
399 if (value == 0 || _specifier
== 'X') {
404 InitDecHexDigits (value);
405 _decPointPos
= _digitsLen
= DecHexLen ();
408 private void Init (string format
, long value)
411 _defPrecision
= Int64DefPrecision
;
412 _positive
= value >= 0;
414 if (value == 0 || _specifier
== 'X') {
415 InitHex ((ulong)value);
421 InitDecHexDigits ((ulong)value);
422 _decPointPos
= _digitsLen
= DecHexLen ();
425 private void Init (string format
, ulong value)
428 _defPrecision
= UInt64DefPrecision
;
431 if (value == 0 || _specifier
== 'X') {
432 InitHex ((ulong)value);
436 InitDecHexDigits (value);
437 _decPointPos
= _digitsLen
= DecHexLen ();
440 #if UNSAFE_TABLES // No unsafe code under TARGET_JVM
443 private void Init (string format
, double value, int defPrecision
)
447 _defPrecision
= defPrecision
;
448 long bits
= BitConverter
.DoubleToInt64Bits (value);
449 _positive
= bits
>= 0;
450 bits
&= Int64
.MaxValue
;
458 int e
= (int)(bits
>> DoubleBitsExponentShift
);
459 long m
= bits
& DoubleBitsMantissaMask
;
460 if (e
== DoubleBitsExponentMask
) {
468 // We need 'm' to be large enough so we won't lose precision.
470 int scale
= ScaleOrder (m
);
471 if (scale
< DoubleDefPrecision
) {
472 expAdjust
= scale
- DoubleDefPrecision
;
473 m
*= GetTenPowerOf (-expAdjust
);
477 m
= (m
+ DoubleBitsMantissaMask
+ 1) * 10;
481 // multiply the mantissa by 10 ^ N
483 ulong hi
= (ulong)m
>> 32;
484 ulong lo2
= MantissaBitsTable
[e
];
485 ulong hi2
= lo2
>> 32;
487 ulong mm
= hi
* lo2
+ lo
* hi2
+ ((lo
* lo2
) >> 32);
488 long res
= (long)(hi
* hi2
+ (mm
>> 32));
489 while (res
< SeventeenDigitsThreshold
) {
490 mm
= (mm
& UInt32
.MaxValue
) * 10;
491 res
= res
* 10 + (long)(mm
>> 32);
494 if ((mm
& 0x80000000) != 0)
497 int order
= DoubleDefPrecision
+ 2;
498 _decPointPos
= TensExponentTable
[e
] + expAdjust
+ order
;
500 // Rescale 'res' to the initial precision (15-17 for doubles).
501 int initialPrecision
= InitialFloatingPrecision ();
502 if (order
> initialPrecision
) {
503 long val
= GetTenPowerOf (order
- initialPrecision
);
504 res
= (res
+ (val
>> 1)) / val
;
505 order
= initialPrecision
;
507 if (res
>= GetTenPowerOf (order
)) {
512 InitDecHexDigits ((ulong)res
);
513 _offset
= CountTrailingZeros ();
514 _digitsLen
= order
- _offset
;
517 private void Init (string format
, decimal value)
520 _defPrecision
= DecimalDefPrecision
;
522 int[] bits
= decimal.GetBits (value);
523 int scale
= (bits
[3] & DecimalBitsScaleMask
) >> 16;
524 _positive
= bits
[3] >= 0;
525 if (bits
[0] == 0 && bits
[1] == 0 && bits
[2] == 0) {
526 _decPointPos
= -scale
;
532 InitDecHexDigits ((uint)bits
[2], ((ulong)bits
[1] << 32) | (uint)bits
[0]);
533 _digitsLen
= DecHexLen ();
534 _decPointPos
= _digitsLen
- scale
;
535 if (_precision
!= -1 || _specifier
!= 'G') {
536 _offset
= CountTrailingZeros ();
537 _digitsLen
-= _offset
;
541 #endregion Constructors
543 #region Inner String Buffer
545 private char[] _cbuf
;
548 private void ResetCharBuf (int size
)
551 if (_cbuf
.Length
< size
)
552 _cbuf
= new char [size
];
555 private void Resize (int len
)
557 char[] newBuf
= new char [len
];
558 Array
.Copy (_cbuf
, newBuf
, _ind
);
562 private void Append (char c
)
564 if (_ind
== _cbuf
.Length
)
569 private void Append (char c
, int cnt
)
571 if (_ind
+ cnt
> _cbuf
.Length
)
572 Resize (_ind
+ cnt
+ 10);
577 private void Append (string s
)
580 if (_ind
+ slen
> _cbuf
.Length
)
581 Resize (_ind
+ slen
+ 10);
582 for (int i
= 0; i
< slen
; i
++)
583 _cbuf
[_ind
++] = s
[i
];
586 #endregion Inner String Buffer
588 #region Helper properties
590 private NumberFormatInfo
GetNumberFormatInstance (IFormatProvider fp
)
592 if (_nfi
!= null && fp
== null)
594 return NumberFormatInfo
.GetInstance (fp
);
597 public CultureInfo CurrentCulture
{
599 if (value != null && value.IsReadOnly
)
600 _nfi
= value.NumberFormat
;
606 private int IntegerDigits
{
607 get { return _decPointPos > 0 ? _decPointPos : 1; }
610 private int DecimalDigits
{
611 get { return _digitsLen > _decPointPos ? _digitsLen - _decPointPos : 0; }
614 private bool IsFloatingSource
{
615 get { return _defPrecision == DoubleDefPrecision || _defPrecision == SingleDefPrecision; }
618 private bool IsZero
{
619 get { return _digitsLen == 0; }
622 private bool IsZeroInteger
{
623 get { return _digitsLen == 0 || _decPointPos <= 0; }
626 #endregion Helper properties
630 private void RoundPos (int pos
)
632 RoundBits (_digitsLen
- pos
);
635 private bool RoundDecimal (int decimals
)
637 return RoundBits (_digitsLen
- _decPointPos
- decimals
);
640 private bool RoundBits (int shift
)
645 if (shift
> _digitsLen
) {
648 _val1
= _val2
= _val3
= _val4
= 0;
653 _digitsLen
+= _offset
;
662 shift
= (shift
- 1) << 2;
663 uint v
= _val1
>> shift
;
664 uint rem16
= v
& 0xf;
665 _val1
= (v ^ rem16
) << shift
;
668 _val1
|= 0x99999999 >> (28 - shift
);
670 int newlen
= DecHexLen ();
671 res
= newlen
!= _digitsLen
;
672 _decPointPos
= _decPointPos
+ newlen
- _digitsLen
;
675 RemoveTrailingZeros ();
679 private void RemoveTrailingZeros ()
681 _offset
= CountTrailingZeros ();
682 _digitsLen
-= _offset
;
683 if (_digitsLen
== 0) {
690 private void AddOneToDecHex ()
692 if (_val1
== 0x99999999) {
694 if (_val2
== 0x99999999) {
696 if (_val3
== 0x99999999) {
698 _val4
= AddOneToDecHex (_val4
);
701 _val3
= AddOneToDecHex (_val3
);
704 _val2
= AddOneToDecHex (_val2
);
707 _val1
= AddOneToDecHex (_val1
);
710 // Assume val != 0x99999999
711 private static uint AddOneToDecHex (uint val
)
713 if ((val
& 0xffff) == 0x9999)
714 if ((val
& 0xffffff) == 0x999999)
715 if ((val
& 0xfffffff) == 0x9999999)
716 return val
+ 0x06666667;
718 return val
+ 0x00666667;
719 else if ((val
& 0xfffff) == 0x99999)
720 return val
+ 0x00066667;
722 return val
+ 0x00006667;
723 else if ((val
& 0xff) == 0x99)
724 if ((val
& 0xfff) == 0x999)
725 return val
+ 0x00000667;
727 return val
+ 0x00000067;
728 else if ((val
& 0xf) == 0x9)
729 return val
+ 0x00000007;
734 private int CountTrailingZeros ()
737 return CountTrailingZeros (_val1
);
739 return CountTrailingZeros (_val2
) + 8;
741 return CountTrailingZeros (_val3
) + 16;
743 return CountTrailingZeros (_val4
) + 24;
747 private static int CountTrailingZeros (uint val
)
749 if ((val
& 0xffff) == 0)
750 if ((val
& 0xffffff) == 0)
751 if ((val
& 0xfffffff) == 0)
755 else if ((val
& 0xfffff) == 0)
759 else if ((val
& 0xff) == 0)
760 if ((val
& 0xfff) == 0)
764 else if ((val
& 0xf) == 0)
772 #region public number formatting methods
775 static NumberFormatter threadNumberFormatter
;
777 private static NumberFormatter
GetInstance()
779 NumberFormatter res
= threadNumberFormatter
;
780 threadNumberFormatter
= null;
782 return new NumberFormatter (Thread
.CurrentThread
);
786 private void Release()
788 threadNumberFormatter
= this;
791 internal static void SetThreadCurrentCulture (CultureInfo culture
)
793 if (threadNumberFormatter
!= null)
794 threadNumberFormatter
.CurrentCulture
= culture
;
797 public static string NumberToString (string format
, sbyte value, IFormatProvider fp
)
799 NumberFormatter inst
= GetInstance();
800 inst
.Init (format
, value, Int8DefPrecision
);
801 string res
= inst
.IntegerToString (format
, fp
);
806 public static string NumberToString (string format
, byte value, IFormatProvider fp
)
808 NumberFormatter inst
= GetInstance();
809 inst
.Init (format
, value, UInt8DefPrecision
);
810 string res
= inst
.IntegerToString (format
, fp
);
815 public static string NumberToString (string format
, ushort value, IFormatProvider fp
)
817 NumberFormatter inst
= GetInstance();
818 inst
.Init (format
, value, Int16DefPrecision
);
819 string res
= inst
.IntegerToString (format
, fp
);
824 public static string NumberToString (string format
, short value, IFormatProvider fp
)
826 NumberFormatter inst
= GetInstance();
827 inst
.Init (format
, value, UInt16DefPrecision
);
828 string res
= inst
.IntegerToString (format
, fp
);
833 public static string NumberToString (string format
, uint value, IFormatProvider fp
)
835 NumberFormatter inst
= GetInstance();
836 inst
.Init (format
, value, Int32DefPrecision
);
837 string res
= inst
.IntegerToString (format
, fp
);
842 public static string NumberToString (string format
, int value, IFormatProvider fp
)
844 NumberFormatter inst
= GetInstance();
845 inst
.Init (format
, value, UInt32DefPrecision
);
846 string res
= inst
.IntegerToString (format
, fp
);
851 public static string NumberToString (string format
, ulong value, IFormatProvider fp
)
853 NumberFormatter inst
= GetInstance();
854 inst
.Init (format
, value);
855 string res
= inst
.IntegerToString (format
, fp
);
860 public static string NumberToString (string format
, long value, IFormatProvider fp
)
862 NumberFormatter inst
= GetInstance();
863 inst
.Init (format
, value);
864 string res
= inst
.IntegerToString (format
, fp
);
869 public static string NumberToString (string format
, float value, IFormatProvider fp
)
871 NumberFormatter inst
= GetInstance();
872 inst
.Init (format
, value, SingleDefPrecision
);
873 NumberFormatInfo nfi
= inst
.GetNumberFormatInstance (fp
);
877 else if (inst
._infinity
)
879 res
= nfi
.PositiveInfinitySymbol
;
881 res
= nfi
.NegativeInfinitySymbol
;
882 else if (inst
._specifier
== 'R')
883 res
= inst
.FormatRoundtrip (value, nfi
);
885 res
= inst
.NumberToString (format
, nfi
);
890 public static string NumberToString (string format
, double value, IFormatProvider fp
)
892 NumberFormatter inst
= GetInstance();
893 inst
.Init (format
, value, DoubleDefPrecision
);
894 NumberFormatInfo nfi
= inst
.GetNumberFormatInstance (fp
);
898 else if (inst
._infinity
)
900 res
= nfi
.PositiveInfinitySymbol
;
902 res
= nfi
.NegativeInfinitySymbol
;
903 else if (inst
._specifier
== 'R')
904 res
= inst
.FormatRoundtrip (value, nfi
);
906 res
= inst
.NumberToString (format
, nfi
);
911 public static string NumberToString (string format
, decimal value, IFormatProvider fp
)
913 NumberFormatter inst
= GetInstance();
914 inst
.Init (format
, value);
915 string res
= inst
.NumberToString (format
, inst
.GetNumberFormatInstance (fp
));
920 public static string NumberToString (uint value, IFormatProvider fp
)
922 if (value >= HundredMillion
)
923 return NumberToString (null, value, fp
);
925 NumberFormatter inst
= GetInstance();
926 string res
= inst
.FastIntegerToString ((int)value, fp
);
931 public static string NumberToString (int value, IFormatProvider fp
)
933 if (value >= HundredMillion
|| value <= -HundredMillion
)
934 return NumberToString (null, value, fp
);
936 NumberFormatter inst
= GetInstance();
937 string res
= inst
.FastIntegerToString (value, fp
);
942 public static string NumberToString (ulong value, IFormatProvider fp
)
944 if (value >= HundredMillion
)
945 return NumberToString (null, value, fp
);
947 NumberFormatter inst
= GetInstance();
948 string res
= inst
.FastIntegerToString ((int)value, fp
);
953 public static string NumberToString (long value, IFormatProvider fp
)
955 if (value >= HundredMillion
|| value <= -HundredMillion
)
956 return NumberToString (null, value, fp
);
958 NumberFormatter inst
= GetInstance();
959 string res
= inst
.FastIntegerToString ((int)value, fp
);
964 public static string NumberToString (float value, IFormatProvider fp
)
966 NumberFormatter inst
= GetInstance();
967 inst
.Init (null, value, SingleDefPrecision
);
968 NumberFormatInfo nfi
= inst
.GetNumberFormatInstance (fp
);
972 else if (inst
._infinity
)
974 res
= nfi
.PositiveInfinitySymbol
;
976 res
= nfi
.NegativeInfinitySymbol
;
978 res
= inst
.FormatGeneral (-1, nfi
);
983 public static string NumberToString (double value, IFormatProvider fp
)
985 NumberFormatter inst
= GetInstance();
986 NumberFormatInfo nfi
= inst
.GetNumberFormatInstance (fp
);
987 inst
.Init (null, value, DoubleDefPrecision
);
991 else if (inst
._infinity
)
993 res
= nfi
.PositiveInfinitySymbol
;
995 res
= nfi
.NegativeInfinitySymbol
;
997 res
= inst
.FormatGeneral (-1, nfi
);
1002 private string FastIntegerToString (int value, IFormatProvider fp
)
1005 string sign
= GetNumberFormatInstance(fp
).NegativeSign
;
1006 ResetCharBuf (8 + sign
.Length
);
1013 if (value >= 10000) {
1014 int v
= value / 10000;
1015 FastAppendDigits (v
, false);
1016 FastAppendDigits (value - v
* 10000, true);
1019 FastAppendDigits (value, false);
1021 return new string (_cbuf
, 0, _ind
);
1024 private string IntegerToString (string format
, IFormatProvider fp
)
1026 NumberFormatInfo nfi
= GetNumberFormatInstance (fp
);
1027 switch (_specifier
) {
1029 return FormatCurrency (_precision
, nfi
);
1031 return FormatDecimal (_precision
, nfi
);
1033 return FormatExponential (_precision
, nfi
);
1035 return FormatFixedPoint (_precision
, nfi
);
1037 if (_precision
<= 0)
1038 return FormatDecimal (-1, nfi
);
1039 return FormatGeneral (_precision
, nfi
);
1041 return FormatNumber (_precision
, nfi
);
1043 return FormatPercent (_precision
, nfi
);
1045 return FormatHexadecimal (_precision
);
1047 if (_isCustomFormat
)
1048 return FormatCustom (format
, nfi
);
1049 throw new FormatException ("The specified format '" + format
+ "' is invalid");
1053 private string NumberToString (string format
, NumberFormatInfo nfi
)
1055 switch (_specifier
) {
1057 return FormatCurrency (_precision
, nfi
);
1059 return FormatExponential (_precision
, nfi
);
1061 return FormatFixedPoint (_precision
, nfi
);
1063 return FormatGeneral (_precision
, nfi
);
1065 return FormatNumber (_precision
, nfi
);
1067 return FormatPercent (_precision
, nfi
);
1070 if (_isCustomFormat
)
1071 return FormatCustom (format
, nfi
);
1072 throw new FormatException ("The specified format '" + format
+ "' is invalid");
1076 public string FormatCurrency (int precision
, NumberFormatInfo nfi
)
1078 precision
= (precision
>= 0 ? precision
: nfi
.CurrencyDecimalDigits
);
1079 RoundDecimal (precision
);
1080 ResetCharBuf (IntegerDigits
* 2 + precision
* 2 + 16);
1083 switch (nfi
.CurrencyPositivePattern
) {
1085 Append (nfi
.CurrencySymbol
);
1088 Append (nfi
.CurrencySymbol
);
1094 switch (nfi
.CurrencyNegativePattern
) {
1097 Append (nfi
.CurrencySymbol
);
1100 Append (nfi
.NegativeSign
);
1101 Append (nfi
.CurrencySymbol
);
1104 Append (nfi
.CurrencySymbol
);
1105 Append (nfi
.NegativeSign
);
1108 Append (nfi
.CurrencySymbol
);
1114 Append (nfi
.NegativeSign
);
1117 Append (nfi
.NegativeSign
);
1120 Append (nfi
.NegativeSign
);
1121 Append (nfi
.CurrencySymbol
);
1125 Append (nfi
.CurrencySymbol
);
1129 Append (nfi
.CurrencySymbol
);
1131 Append (nfi
.NegativeSign
);
1135 Append (nfi
.CurrencySymbol
);
1144 AppendIntegerStringWithGroupSeparator (nfi
.RawCurrencyGroupSizes
, nfi
.CurrencyGroupSeparator
);
1146 if (precision
> 0) {
1147 Append (nfi
.CurrencyDecimalSeparator
);
1148 AppendDecimalString (precision
);
1152 switch (nfi
.CurrencyPositivePattern
) {
1154 Append (nfi
.CurrencySymbol
);
1158 Append (nfi
.CurrencySymbol
);
1163 switch (nfi
.CurrencyNegativePattern
) {
1168 Append (nfi
.NegativeSign
);
1171 Append (nfi
.CurrencySymbol
);
1175 Append (nfi
.CurrencySymbol
);
1178 Append (nfi
.NegativeSign
);
1179 Append (nfi
.CurrencySymbol
);
1182 Append (nfi
.CurrencySymbol
);
1183 Append (nfi
.NegativeSign
);
1187 Append (nfi
.CurrencySymbol
);
1191 Append (nfi
.CurrencySymbol
);
1192 Append (nfi
.NegativeSign
);
1195 Append (nfi
.NegativeSign
);
1198 Append (nfi
.NegativeSign
);
1200 Append (nfi
.CurrencySymbol
);
1207 Append (nfi
.CurrencySymbol
);
1213 return new string (_cbuf
, 0, _ind
);
1216 private string FormatDecimal (int precision
, NumberFormatInfo nfi
)
1218 if (precision
< _digitsLen
)
1219 precision
= _digitsLen
;
1223 ResetCharBuf (precision
+ 1);
1225 Append (nfi
.NegativeSign
);
1226 AppendDigits (0, precision
);
1228 return new string (_cbuf
, 0, _ind
);
1231 #if UNSAFE_TABLES // No unsafe code under TARGET_JVM
1234 private string FormatHexadecimal (int precision
)
1236 int size
= Math
.Max (precision
, _decPointPos
);
1238 char* digits
= _specifierIsUpper
? DigitUpperTable
: DigitLowerTable
;
1240 char[] digits
= _specifierIsUpper
? DigitUpperTable
: DigitLowerTable
;
1242 ResetCharBuf (size
);
1244 ulong val
= _val1
| ((ulong)_val2
<< 32);
1246 _cbuf
[--size
] = digits
[val
& 0xf];
1249 return new string (_cbuf
, 0, _ind
);
1252 public string FormatFixedPoint (int precision
, NumberFormatInfo nfi
)
1254 if (precision
== -1)
1255 precision
= nfi
.NumberDecimalDigits
;
1257 RoundDecimal (precision
);
1259 ResetCharBuf (IntegerDigits
+ precision
+ 2);
1262 Append (nfi
.NegativeSign
);
1264 AppendIntegerString (IntegerDigits
);
1266 if (precision
> 0) {
1267 Append (nfi
.NumberDecimalSeparator
);
1268 AppendDecimalString (precision
);
1271 return new string (_cbuf
, 0, _ind
);
1274 private string FormatRoundtrip (double origval
, NumberFormatInfo nfi
)
1276 NumberFormatter nfc
= GetClone ();
1277 if (origval
>= MinRoundtripVal
&& origval
<= MaxRoundtripVal
) {
1278 string shortRep
= FormatGeneral (_defPrecision
, nfi
);
1279 if (origval
== Double
.Parse (shortRep
, nfi
))
1282 return nfc
.FormatGeneral (_defPrecision
+ 2, nfi
);
1285 private string FormatRoundtrip (float origval
, NumberFormatInfo nfi
)
1287 NumberFormatter nfc
= GetClone ();
1288 string shortRep
= FormatGeneral (_defPrecision
, nfi
);
1289 // Check roundtrip only for "normal" double values.
1290 if (origval
== Single
.Parse (shortRep
, nfi
))
1292 return nfc
.FormatGeneral (_defPrecision
+ 2, nfi
);
1295 private string FormatGeneral (int precision
, NumberFormatInfo nfi
)
1298 if (precision
== -1) {
1299 enableExp
= IsFloatingSource
;
1300 precision
= _defPrecision
;
1305 precision
= _defPrecision
;
1306 RoundPos (precision
);
1309 int intDigits
= _decPointPos
;
1310 int digits
= _digitsLen
;
1311 int decDigits
= digits
- intDigits
;
1313 if ((intDigits
> precision
|| intDigits
<= -4) && enableExp
)
1314 return FormatExponential (digits
- 1, nfi
, 2);
1320 ResetCharBuf (decDigits
+ intDigits
+ 3);
1323 Append (nfi
.NegativeSign
);
1328 AppendDigits (digits
- intDigits
, digits
);
1330 if (decDigits
> 0) {
1331 Append (nfi
.NumberDecimalSeparator
);
1332 AppendDigits (0, decDigits
);
1335 return new string (_cbuf
, 0, _ind
);
1338 public string FormatNumber (int precision
, NumberFormatInfo nfi
)
1340 precision
= (precision
>= 0 ? precision
: nfi
.NumberDecimalDigits
);
1341 ResetCharBuf (IntegerDigits
* 3 + precision
);
1342 RoundDecimal (precision
);
1345 switch (nfi
.NumberNegativePattern
) {
1350 Append (nfi
.NegativeSign
);
1353 Append (nfi
.NegativeSign
);
1359 AppendIntegerStringWithGroupSeparator (nfi
.RawNumberGroupSizes
, nfi
.NumberGroupSeparator
);
1361 if (precision
> 0) {
1362 Append (nfi
.NumberDecimalSeparator
);
1363 AppendDecimalString (precision
);
1367 switch (nfi
.NumberNegativePattern
) {
1372 Append (nfi
.NegativeSign
);
1376 Append (nfi
.NegativeSign
);
1381 return new string (_cbuf
, 0, _ind
);
1384 public string FormatPercent (int precision
, NumberFormatInfo nfi
)
1386 precision
= (precision
>= 0 ? precision
: nfi
.PercentDecimalDigits
);
1388 RoundDecimal (precision
);
1389 ResetCharBuf (IntegerDigits
* 2 + precision
+ 16);
1392 if (nfi
.PercentPositivePattern
== 2)
1393 Append (nfi
.PercentSymbol
);
1396 switch (nfi
.PercentNegativePattern
) {
1398 Append (nfi
.NegativeSign
);
1401 Append (nfi
.NegativeSign
);
1404 Append (nfi
.NegativeSign
);
1405 Append (nfi
.PercentSymbol
);
1410 AppendIntegerStringWithGroupSeparator (nfi
.RawPercentGroupSizes
, nfi
.PercentGroupSeparator
);
1412 if (precision
> 0) {
1413 Append (nfi
.PercentDecimalSeparator
);
1414 AppendDecimalString (precision
);
1418 switch (nfi
.PercentPositivePattern
) {
1421 Append (nfi
.PercentSymbol
);
1424 Append (nfi
.PercentSymbol
);
1429 switch (nfi
.PercentNegativePattern
) {
1432 Append (nfi
.PercentSymbol
);
1435 Append (nfi
.PercentSymbol
);
1440 return new string (_cbuf
, 0, _ind
);
1443 public string FormatExponential (int precision
, NumberFormatInfo nfi
)
1445 if (precision
== -1)
1446 precision
= DefaultExpPrecision
;
1448 RoundPos (precision
+ 1);
1449 return FormatExponential (precision
, nfi
, 3);
1452 private string FormatExponential (int precision
, NumberFormatInfo nfi
, int expDigits
)
1454 int decDigits
= _decPointPos
;
1455 int digits
= _digitsLen
;
1456 int exponent
= decDigits
- 1;
1457 decDigits
= _decPointPos
= 1;
1459 ResetCharBuf (precision
+ 8);
1462 Append (nfi
.NegativeSign
);
1464 AppendOneDigit (digits
- 1);
1466 if (precision
> 0) {
1467 Append (nfi
.NumberDecimalSeparator
);
1468 AppendDigits (digits
- precision
- 1, digits
- _decPointPos
);
1471 AppendExponent (nfi
, exponent
, expDigits
);
1473 return new string (_cbuf
, 0, _ind
);
1476 public string FormatCustom (string format
, NumberFormatInfo nfi
)
1481 CustomInfo
.GetActiveSection (format
, ref p
, IsZero
, ref offset
, ref length
);
1483 return _positive
? string.Empty
: nfi
.NegativeSign
;
1486 CustomInfo info
= CustomInfo
.Parse (format
, offset
, length
, nfi
);
1488 Console
.WriteLine ("Format : {0}",format
);
1489 Console
.WriteLine ("DecimalDigits : {0}",info
.DecimalDigits
);
1490 Console
.WriteLine ("DecimalPointPos : {0}",info
.DecimalPointPos
);
1491 Console
.WriteLine ("DecimalTailSharpDigits : {0}",info
.DecimalTailSharpDigits
);
1492 Console
.WriteLine ("IntegerDigits : {0}",info
.IntegerDigits
);
1493 Console
.WriteLine ("IntegerHeadSharpDigits : {0}",info
.IntegerHeadSharpDigits
);
1494 Console
.WriteLine ("IntegerHeadPos : {0}",info
.IntegerHeadPos
);
1495 Console
.WriteLine ("UseExponent : {0}",info
.UseExponent
);
1496 Console
.WriteLine ("ExponentDigits : {0}",info
.ExponentDigits
);
1497 Console
.WriteLine ("ExponentTailSharpDigits : {0}",info
.ExponentTailSharpDigits
);
1498 Console
.WriteLine ("ExponentNegativeSignOnly : {0}",info
.ExponentNegativeSignOnly
);
1499 Console
.WriteLine ("DividePlaces : {0}",info
.DividePlaces
);
1500 Console
.WriteLine ("Percents : {0}",info
.Percents
);
1501 Console
.WriteLine ("Permilles : {0}",info
.Permilles
);
1503 StringBuilder sb_int
= new StringBuilder (info
.IntegerDigits
* 2);
1504 StringBuilder sb_dec
= new StringBuilder (info
.DecimalDigits
* 2);
1505 StringBuilder sb_exp
= (info
.UseExponent
? new StringBuilder (info
.ExponentDigits
* 2) : null);
1508 if (info
.Percents
> 0)
1509 Multiply10(2 * info
.Percents
);
1510 if (info
.Permilles
> 0)
1511 Multiply10(3 * info
.Permilles
);
1512 if (info
.DividePlaces
> 0)
1513 Divide10(info
.DividePlaces
);
1515 bool expPositive
= true;
1516 if (info
.UseExponent
&& (info
.DecimalDigits
> 0 || info
.IntegerDigits
> 0)) {
1518 RoundPos (info
.DecimalDigits
+ info
.IntegerDigits
);
1519 diff
-= _decPointPos
- info
.IntegerDigits
;
1520 _decPointPos
= info
.IntegerDigits
;
1523 expPositive
= diff
<= 0;
1524 AppendNonNegativeNumber (sb_exp
, diff
< 0 ? -diff
: diff
);
1527 RoundDecimal (info
.DecimalDigits
);
1529 if (info
.IntegerDigits
!= 0 || !IsZeroInteger
)
1530 AppendIntegerString (IntegerDigits
, sb_int
);
1532 AppendDecimalString (DecimalDigits
, sb_dec
);
1534 if (info
.UseExponent
) {
1535 if (info
.DecimalDigits
<= 0 && info
.IntegerDigits
<= 0)
1538 if (sb_int
.Length
< info
.IntegerDigits
)
1539 sb_int
.Insert (0, "0", info
.IntegerDigits
- sb_int
.Length
);
1541 while (sb_exp
.Length
< info
.ExponentDigits
- info
.ExponentTailSharpDigits
)
1542 sb_exp
.Insert (0, '0');
1544 if (expPositive
&& !info
.ExponentNegativeSignOnly
)
1545 sb_exp
.Insert (0, nfi
.PositiveSign
);
1546 else if (!expPositive
)
1547 sb_exp
.Insert (0, nfi
.NegativeSign
);
1550 if (sb_int
.Length
< info
.IntegerDigits
- info
.IntegerHeadSharpDigits
)
1551 sb_int
.Insert (0, "0", info
.IntegerDigits
- info
.IntegerHeadSharpDigits
- sb_int
.Length
);
1552 if (info
.IntegerDigits
== info
.IntegerHeadSharpDigits
&& IsZeroOnly (sb_int
))
1553 sb_int
.Remove (0, sb_int
.Length
);
1556 ZeroTrimEnd (sb_dec
, true);
1557 while (sb_dec
.Length
< info
.DecimalDigits
- info
.DecimalTailSharpDigits
)
1558 sb_dec
.Append ('0');
1559 if (sb_dec
.Length
> info
.DecimalDigits
)
1560 sb_dec
.Remove (info
.DecimalDigits
, sb_dec
.Length
- info
.DecimalDigits
);
1562 return info
.Format (format
, offset
, length
, nfi
, _positive
, sb_int
, sb_dec
, sb_exp
);
1564 #endregion public number formatting methods
1566 #region StringBuilder formatting helpers
1568 private static void ZeroTrimEnd (StringBuilder sb
, bool canEmpty
)
1571 for (int i
= sb
.Length
- 1; (canEmpty
? i
>= 0 : i
> 0); i
--) {
1578 sb
.Remove (sb
.Length
- len
, len
);
1581 private static bool IsZeroOnly (StringBuilder sb
)
1583 for (int i
= 0; i
< sb
.Length
; i
++)
1584 if (char.IsDigit (sb
[i
]) && sb
[i
] != '0')
1589 private static void AppendNonNegativeNumber (StringBuilder sb
, int v
)
1592 throw new ArgumentException ();
1594 int i
= ScaleOrder (v
) - 1;
1596 int n
= v
/ (int)GetTenPowerOf (i
);
1597 sb
.Append ((char)('0' | n
));
1598 v
-= (int)GetTenPowerOf (i
--) * n
;
1602 #endregion StringBuilder formatting helpers
1604 #region Append helpers
1606 private void AppendIntegerString (int minLength
, StringBuilder sb
)
1608 if (_decPointPos
<= 0) {
1609 sb
.Append ('0', minLength
);
1613 if (_decPointPos
< minLength
)
1614 sb
.Append ('0', minLength
- _decPointPos
);
1616 AppendDigits (_digitsLen
- _decPointPos
, _digitsLen
, sb
);
1619 private void AppendIntegerString (int minLength
)
1621 if (_decPointPos
<= 0) {
1622 Append ('0', minLength
);
1626 if (_decPointPos
< minLength
)
1627 Append ('0', minLength
- _decPointPos
);
1629 AppendDigits (_digitsLen
- _decPointPos
, _digitsLen
);
1632 private void AppendDecimalString (int precision
, StringBuilder sb
)
1634 AppendDigits (_digitsLen
- precision
- _decPointPos
, _digitsLen
- _decPointPos
, sb
);
1637 private void AppendDecimalString (int precision
)
1639 AppendDigits (_digitsLen
- precision
- _decPointPos
, _digitsLen
- _decPointPos
);
1642 private void AppendIntegerStringWithGroupSeparator (int[] groups
, string groupSeparator
)
1644 if (IsZeroInteger
) {
1651 for (int i
= 0; i
< groups
.Length
; i
++) {
1652 total
+= groups
[i
];
1653 if (total
<= _decPointPos
)
1659 if (groups
.Length
> 0 && total
> 0) {
1661 int groupSize
= groups
[groupIndex
];
1662 int fraction
= _decPointPos
> total
? _decPointPos
- total
: 0;
1663 if (groupSize
== 0) {
1664 while (groupIndex
>= 0 && groups
[groupIndex
] == 0)
1667 groupSize
= fraction
> 0 ? fraction
: groups
[groupIndex
];
1670 counter
= groupSize
;
1672 groupIndex
+= fraction
/ groupSize
;
1673 counter
= fraction
% groupSize
;
1675 counter
= groupSize
;
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 #if UNSAFE_TABLES // No unsafe code under TARGET_JVM
1754 private void FastAppendDigits (int val
, bool force
)
1758 if (force
|| val
>= 100) {
1759 int v
= (val
* 5243) >> 19;
1760 digits
= DecHexDigits
[v
];
1761 if (force
|| val
>= 1000)
1762 _cbuf
[i
++] = (char)('0' | digits
>> 4);
1763 _cbuf
[i
++] = (char)('0' | (digits
& 0xf));
1764 digits
= DecHexDigits
[val
- v
* 100];
1767 digits
= DecHexDigits
[val
];
1769 if (force
|| val
>= 10)
1770 _cbuf
[i
++] = (char)('0' | digits
>> 4);
1771 _cbuf
[i
++] = (char)('0' | (digits
& 0xf));
1775 private void AppendDigits (int start
, int end
)
1780 int i
= _ind
+ (end
- start
);
1781 if (i
> _cbuf
.Length
)
1788 for (int next
= start
+ 8 - (start
& 0x7); ; start
= next
, next
+= 8) {
1792 else if (next
== 16)
1794 else if (next
== 24)
1796 else if (next
== 32)
1800 v
>>= (start
& 0x7) << 2;
1804 _cbuf
[--i
] = (char)('0' | v
& 0xf);
1805 switch (next
- start
) {
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);
1825 _cbuf
[--i
] = (char)('0' | (v
>>= 4) & 0xf);
1835 private void AppendDigits (int start
, int end
, StringBuilder sb
)
1840 int i
= sb
.Length
+ (end
- start
);
1846 for (int next
= start
+ 8 - (start
& 0x7); ; start
= next
, next
+= 8) {
1850 else if (next
== 16)
1852 else if (next
== 24)
1854 else if (next
== 32)
1858 v
>>= (start
& 0x7) << 2;
1861 sb
[--i
] = (char)('0' | v
& 0xf);
1862 switch (next
- start
) {
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);
1882 sb
[--i
] = (char)('0' | (v
>>= 4) & 0xf);
1892 #endregion Append helpers
1896 private void Multiply10(int count
)
1898 if (count
<= 0 || _digitsLen
== 0)
1901 _decPointPos
+= count
;
1904 private void Divide10(int count
)
1906 if (count
<= 0 || _digitsLen
== 0)
1909 _decPointPos
-= count
;
1912 private NumberFormatter
GetClone ()
1914 return (NumberFormatter
)this.MemberwiseClone ();
1921 private class CustomInfo
1923 public bool UseGroup
= false;
1924 public int DecimalDigits
= 0;
1925 public int DecimalPointPos
= -1;
1926 public int DecimalTailSharpDigits
= 0;
1927 public int IntegerDigits
= 0;
1928 public int IntegerHeadSharpDigits
= 0;
1929 public int IntegerHeadPos
= 0;
1930 public bool UseExponent
= false;
1931 public int ExponentDigits
= 0;
1932 public int ExponentTailSharpDigits
= 0;
1933 public bool ExponentNegativeSignOnly
= true;
1934 public int DividePlaces
= 0;
1935 public int Percents
= 0;
1936 public int Permilles
= 0;
1938 public static void GetActiveSection (string format
, ref bool positive
, bool zero
, ref int offset
, ref int length
)
1940 int[] lens
= new int [3];
1943 char literal
= '\0';
1944 for (int i
= 0; i
< format
.Length
; i
++) {
1945 char c
= format
[i
];
1947 if (c
== literal
|| (literal
== '\0' && (c
== '\"' || c
== '\''))) {
1948 if (literal
== '\0')
1955 if (literal
== '\0' && format
[i
] == ';' && (i
== 0 || format
[i
- 1] != '\\')) {
1956 lens
[index
++] = i
- lastPos
;
1965 length
= format
.Length
;
1969 if (positive
|| zero
) {
1974 if (lens
[0] + 1 < format
.Length
) {
1976 offset
= lens
[0] + 1;
1977 length
= format
.Length
- offset
;
1988 offset
= lens
[0] + lens
[1] + 2;
1989 length
= format
.Length
- offset
;
1999 offset
= lens
[0] + 1;
2011 offset
= lens
[0] + lens
[1] + 2;
2022 offset
= lens
[0] + 1;
2033 throw new ArgumentException ();
2036 public static CustomInfo
Parse (string format
, int offset
, int length
, NumberFormatInfo nfi
)
2038 char literal
= '\0';
2039 bool integerArea
= true;
2040 bool decimalArea
= false;
2041 bool exponentArea
= false;
2042 bool sharpContinues
= true;
2044 CustomInfo info
= new CustomInfo ();
2045 int groupSeparatorCounter
= 0;
2047 for (int i
= offset
; i
- offset
< length
; i
++) {
2048 char c
= format
[i
];
2050 if (c
== literal
&& c
!= '\0') {
2054 if (literal
!= '\0')
2057 if (exponentArea
&& (c
!= '\0' && c
!= '0' && c
!= '#')) {
2058 exponentArea
= false;
2059 integerArea
= (info
.DecimalPointPos
< 0);
2060 decimalArea
= !integerArea
;
2071 if (c
== '\"' || c
== '\'') {
2076 if (sharpContinues
&& integerArea
)
2077 info
.IntegerHeadSharpDigits
++;
2078 else if (decimalArea
)
2079 info
.DecimalTailSharpDigits
++;
2080 else if (exponentArea
)
2081 info
.ExponentTailSharpDigits
++;
2086 sharpContinues
= false;
2088 info
.DecimalTailSharpDigits
= 0;
2089 else if (exponentArea
)
2090 info
.ExponentTailSharpDigits
= 0;
2092 if (info
.IntegerHeadPos
== -1)
2093 info
.IntegerHeadPos
= i
;
2096 info
.IntegerDigits
++;
2097 if (groupSeparatorCounter
> 0)
2098 info
.UseGroup
= true;
2099 groupSeparatorCounter
= 0;
2101 else if (decimalArea
)
2102 info
.DecimalDigits
++;
2103 else if (exponentArea
)
2104 info
.ExponentDigits
++;
2108 if (info
.UseExponent
)
2111 info
.UseExponent
= true;
2112 integerArea
= false;
2113 decimalArea
= false;
2114 exponentArea
= true;
2115 if (i
+ 1 - offset
< length
) {
2116 char nc
= format
[i
+ 1];
2118 info
.ExponentNegativeSignOnly
= false;
2119 if (nc
== '+' || nc
== '-')
2121 else if (nc
!= '0' && nc
!= '#') {
2122 info
.UseExponent
= false;
2123 if (info
.DecimalPointPos
< 0)
2130 integerArea
= false;
2132 exponentArea
= false;
2133 if (info
.DecimalPointPos
== -1)
2134 info
.DecimalPointPos
= i
;
2143 if (integerArea
&& info
.IntegerDigits
> 0)
2144 groupSeparatorCounter
++;
2151 if (info
.ExponentDigits
== 0)
2152 info
.UseExponent
= false;
2154 info
.IntegerHeadSharpDigits
= 0;
2156 if (info
.DecimalDigits
== 0)
2157 info
.DecimalPointPos
= -1;
2159 info
.DividePlaces
+= groupSeparatorCounter
* 3;
2164 public string Format (string format
, int offset
, int length
, NumberFormatInfo nfi
, bool positive
, StringBuilder sb_int
, StringBuilder sb_dec
, StringBuilder sb_exp
)
2166 StringBuilder sb
= new StringBuilder ();
2167 char literal
= '\0';
2168 bool integerArea
= true;
2169 bool decimalArea
= false;
2170 int intSharpCounter
= 0;
2171 int sb_int_index
= 0;
2172 int sb_dec_index
= 0;
2174 int[] groups
= nfi
.RawNumberGroupSizes
;
2175 string groupSeparator
= nfi
.NumberGroupSeparator
;
2176 int intLen
= 0, total
= 0, groupIndex
= 0, counter
= 0, groupSize
= 0;
2177 if (UseGroup
&& groups
.Length
> 0) {
2178 intLen
= sb_int
.Length
;
2179 for (int i
= 0; i
< groups
.Length
; i
++) {
2180 total
+= groups
[i
];
2181 if (total
<= intLen
)
2184 groupSize
= groups
[groupIndex
];
2185 int fraction
= intLen
> total
? intLen
- total
: 0;
2186 if (groupSize
== 0) {
2187 while (groupIndex
>= 0 && groups
[groupIndex
] == 0)
2190 groupSize
= fraction
> 0 ? fraction
: groups
[groupIndex
];
2193 counter
= groupSize
;
2195 groupIndex
+= fraction
/ groupSize
;
2196 counter
= fraction
% groupSize
;
2198 counter
= groupSize
;
2206 for (int i
= offset
; i
- offset
< length
; i
++) {
2207 char c
= format
[i
];
2209 if (c
== literal
&& c
!= '\0') {
2213 if (literal
!= '\0') {
2221 if (i
- offset
< length
)
2222 sb
.Append (format
[i
]);
2226 if (c
== '\"' || c
== '\'')
2234 if (IntegerDigits
- intSharpCounter
< sb_int
.Length
+ sb_int_index
|| c
== '0')
2235 while (IntegerDigits
- intSharpCounter
+ sb_int_index
< sb_int
.Length
) {
2236 sb
.Append (sb_int
[sb_int_index
++]);
2237 if (UseGroup
&& --intLen
> 0 && --counter
== 0) {
2238 sb
.Append (groupSeparator
);
2239 if (--groupIndex
< groups
.Length
&& groupIndex
>= 0)
2240 groupSize
= groups
[groupIndex
];
2241 counter
= groupSize
;
2246 else if (decimalArea
) {
2247 if (sb_dec_index
< sb_dec
.Length
)
2248 sb
.Append (sb_dec
[sb_dec_index
++]);
2256 if (sb_exp
== null || !UseExponent
) {
2265 for (q
= i
+ 1; q
- offset
< length
; q
++) {
2266 if (format
[q
] == '0') {
2270 if (q
== i
+ 1 && (format
[q
] == '+' || format
[q
] == '-'))
2279 integerArea
= (DecimalPointPos
< 0);
2280 decimalArea
= !integerArea
;
2291 if (DecimalPointPos
== i
) {
2292 if (DecimalDigits
> 0) {
2293 while (sb_int_index
< sb_int
.Length
)
2294 sb
.Append (sb_int
[sb_int_index
++]);
2296 if (sb_dec
.Length
> 0)
2297 sb
.Append (nfi
.NumberDecimalSeparator
);
2299 integerArea
= false;
2305 sb
.Append (nfi
.PercentSymbol
);
2308 sb
.Append (nfi
.PerMilleSymbol
);
2317 sb
.Insert (0, nfi
.NegativeSign
);
2319 return sb
.ToString ();