2 // System.FloatingPointFormatter.cs
5 // Pedro Martinez Julia <yoros@wanadoo.es>
6 // Jon Skeet <skeet@pobox.com>
7 // Sebastien Pouliot <sebastien@ximian.com>
9 // Copyright (C) 2003 Pedro MartÃez Juliá <yoros@wanadoo.es>
10 // Copyright (C) 2004 Jon Skeet
11 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 using System
.Collections
;
36 using System
.Globalization
;
41 internal class FloatingPointFormatter
{
47 public int dec_len_min
;
53 public FloatingPointFormatter
54 (double p
, double p10
, int dec_len
, int dec_len_min
,
55 double p2
, double p102
, int dec_len2
, int dec_len_min2
) {
59 format1
.dec_len
= dec_len
;
60 format1
.dec_len_min
= dec_len_min
;
64 format2
.dec_len
= dec_len2
;
65 format2
.dec_len_min
= dec_len_min2
;
68 public string GetStringFrom
69 (string format
, NumberFormatInfo nfi
, double value) {
71 if (format
== null || format
== "") {
75 nfi
= NumberFormatInfo
.CurrentInfo
;
77 if (Double
.IsNaN(value)) {
80 if (Double
.IsNegativeInfinity(value)) {
81 return nfi
.NegativeInfinitySymbol
;
83 if (Double
.IsPositiveInfinity(value)) {
84 return nfi
.PositiveInfinitySymbol
;
89 if (!ParseFormat(format
, out specifier
, out precision
)) {
90 return FormatCustom (format1
, value, nfi
, format
);
93 Format formatData
= format1
;//(precision > format1.dec_len+1) ? format2 : format1;
97 return FormatCurrency (formatData
, value, nfi
, precision
);
99 throw new FormatException(Locale
.GetText(
100 "The specified format is invalid") + ": " + format
);
102 formatData
= (precision
> format1
.dec_len
) ? format2
: format1
;
103 return FormatExponential (formatData
, value, nfi
, precision
, format
[0]);
105 return FormatFixedPoint (formatData
, value, nfi
, precision
);
107 return FormatGeneral (formatData
, value, nfi
, precision
);
109 return FormatNumber (formatData
, value, nfi
, precision
);
111 return FormatPercent (formatData
, value, nfi
, precision
);
113 return FormatReversible (value, nfi
, precision
);
115 throw new FormatException(Locale
.GetText(
116 "The specified format is invalid") + ": " + format
);
118 throw new FormatException(Locale
.GetText(
119 "The specified format is invalid") + ": " + format
);
123 private bool ParseFormat (string format
,
124 out char specifier
, out int precision
) {
126 precision
= format2
.dec_len
;
128 // FIXME: Math.Round is used and the max is 15.
133 switch (format
.Length
) {
135 specifier
= Char
.ToUpperInvariant(format
[0]);
139 if (Char
.IsLetter(format
[0]) && Char
.IsDigit(format
[1])) {
140 specifier
= Char
.ToUpperInvariant(format
[0]);
141 precision
= Convert
.ToInt32(format
[1] - '0');
146 if (Char
.IsLetter(format
[0]) && Char
.IsDigit(format
[1])
147 && Char
.IsDigit(format
[2])) {
148 specifier
= Char
.ToUpperInvariant(format
[0]);
149 precision
= Convert
.ToInt32(format
.Substring(1, 2));
157 // Math.Round use banker's rounding while this is not what must
158 // be used for string formatting (see bug #60111)
159 // http://bugzilla.ximian.com/show_bug.cgi?id=60111
161 // FIXME: should be moved out of here post Mono 1.0
162 private double Round (double value)
164 double int_part
= Math
.Floor (value);
165 double dec_part
= value - int_part
;
166 if (dec_part
>= 0.5) {
172 // FIXME: should be moved out of here post Mono 1.0
173 private double Round (double value, int digits
)
176 return Round (value);
177 double p
= Math
.Pow (10, digits
);
178 double int_part
= Math
.Floor (value);
179 double dec_part
= value - int_part
;
180 dec_part
*= 1000000000000000L;
181 dec_part
= Math
.Floor (dec_part
);
182 dec_part
/= (1000000000000000L / p
);
183 dec_part
= Round (dec_part
);
185 return int_part
+ dec_part
;
188 private void Normalize (Format formatData
, double value, int precision
,
189 out long mantissa
, out int exponent
) {
193 Double
.IsInfinity(value) ||
194 Double
.IsNaN(value)) {
197 value = Math
.Abs(value);
198 if (precision
<= (formatData
.dec_len
) && precision
>= 0) {
199 value = Round (value, precision
);
203 Double
.IsInfinity(value) ||
204 Double
.IsNaN(value)) {
208 if (value >= formatData
.p10
) {
209 while (value >= formatData
.p10
) {
214 else if (value < formatData
.p
) {
215 while (value < formatData
.p
) {
220 mantissa
= (long) Round(value);
223 private string FormatCurrency (Format formatData
, double value,
224 NumberFormatInfo nfi
, int precision
) {
226 precision
= (precision
>= 0) ? precision
: nfi
.CurrencyDecimalDigits
;
227 string numb
= FormatNumberInternal (formatData
, value, nfi
, precision
);
229 switch (nfi
.CurrencyNegativePattern
) {
231 return "(" + nfi
.CurrencySymbol
+ numb
+ ")";
233 return nfi
.NegativeSign
+ nfi
.CurrencySymbol
+ numb
;
235 return nfi
.CurrencySymbol
+ nfi
.NegativeSign
+ numb
;
237 return nfi
.CurrencySymbol
+ numb
+ nfi
.NegativeSign
;
239 return "(" + numb
+ nfi
.CurrencySymbol
+ ")";
241 return nfi
.NegativeSign
+ numb
+ nfi
.CurrencySymbol
;
243 return numb
+ nfi
.NegativeSign
+ nfi
.CurrencySymbol
;
245 return numb
+ nfi
.CurrencySymbol
+ nfi
.NegativeSign
;
247 return nfi
.NegativeSign
+ numb
+ " " + nfi
.CurrencySymbol
;
249 return nfi
.NegativeSign
+ nfi
.CurrencySymbol
+ " " + numb
;
251 return numb
+ " " + nfi
.CurrencySymbol
+ nfi
.NegativeSign
;
253 return nfi
.CurrencySymbol
+ " " + numb
+ nfi
.NegativeSign
;
255 return nfi
.CurrencySymbol
+ " " + nfi
.NegativeSign
+ numb
;
257 return numb
+ nfi
.NegativeSign
+ " " + nfi
.CurrencySymbol
;
259 return "(" + nfi
.CurrencySymbol
+ " " + numb
+ ")";
261 return "(" + numb
+ " " + nfi
.CurrencySymbol
+ ")";
263 throw new ArgumentException(Locale
.GetText(
264 "Invalid CurrencyNegativePattern"));
268 switch (nfi
.CurrencyPositivePattern
) {
270 return nfi
.CurrencySymbol
+ numb
;
272 return numb
+ nfi
.CurrencySymbol
;
274 return nfi
.CurrencySymbol
+ " " + numb
;
276 return numb
+ " " + nfi
.CurrencySymbol
;
278 throw new ArgumentException(Locale
.GetText(
279 "invalid CurrencyPositivePattern"));
284 private string FormatExponential (Format formatData
, double value, NumberFormatInfo nfi
,
285 int precision
, char exp_char
) {
286 StringBuilder sb
= new StringBuilder();
287 precision
= (precision
>= 0) ? precision
: 6;
288 int decimals
= precision
;
291 Normalize (formatData
, value, precision
, out mantissa
, out exponent
);
292 if (formatData
.dec_len
> precision
) {
293 double aux
= mantissa
;
294 for (int i
= 0; i
< formatData
.dec_len
- precision
; i
++) {
297 mantissa
= (long) Round(aux
);
298 for (int i
= 0; i
< formatData
.dec_len
- precision
; i
++) {
302 bool not_null
= false;
303 if (mantissa
!= 0.0) {
304 for (int i
= 0; i
< formatData
.dec_len
|| mantissa
>= 10; i
++) {
305 if ((not_null
== false) && ((mantissa
% 10) != 0)) {
309 sb
.Insert(0, (char)('0' + (mantissa
% 10)));
317 sb
= new StringBuilder();
318 sb
.Append((char)('0' + (mantissa
% 10)));
321 while (precision
> 0) {
325 if (sb
.Length
== 0) {
328 sb
.Insert (0, (char)('0' + (mantissa
% 10)) +
329 nfi
.NumberDecimalSeparator
);
332 sb
.Append(exp_char
+ nfi
.PositiveSign
);
335 sb
.Append(exp_char
+ nfi
.NegativeSign
);
337 sb
.Append(Math
.Abs(exponent
).ToString("000"));
339 sb
.Insert(0, nfi
.NegativeSign
);
341 return sb
.ToString();
344 private string FormatFixedPoint (Format formatData
, double value,
345 NumberFormatInfo nfi
, int precision
) {
346 StringBuilder sb
= new StringBuilder();
347 precision
= (precision
>= 0) ? precision
: nfi
.NumberDecimalDigits
;
348 int decimals
= precision
;
351 Normalize (formatData
, value, precision
, out mantissa
, out exponent
);
353 while (decimals
> 0) {
359 int decimal_limit
= -(decimals
+ 1);
360 while (exponent
< 0) {
361 if (exponent
> decimal_limit
) {
362 sb
.Insert(0, (char)('0' + (mantissa
% 10)));
369 sb
.Append ('0', decimals
);
373 if (precision
!= 0) {
374 sb
.Insert(0, nfi
.NumberDecimalSeparator
);
380 while (exponent
> 0) {
384 while (mantissa
!= 0) {
385 sb
.Insert(0, (char)('0' + (mantissa
% 10)));
390 sb
.Insert(0, nfi
.NegativeSign
);
392 return sb
.ToString();
395 private string FormatGeneral (Format formatData
, double value,
396 NumberFormatInfo nfi
, int precision
) {
397 StringBuilder sb
= new StringBuilder();
402 precision
= (precision
> 0) ?
403 precision
: formatData
.dec_len
+1;
407 Normalize (formatData
, value, precision
, out mantissa
, out exponent
);
409 double dmant
= mantissa
;
410 for (int i
= 0; i
< formatData
.dec_len
- precision
+ 1; i
++) {
413 mantissa
= (long) Round (dmant
);
414 for (int i
= 0; i
< formatData
.dec_len
- precision
+ 1; i
++) {
419 /* Calculate the exponent we would get using the scientific notation */
420 int snExponent
= exponent
;
421 long snMantissa
= mantissa
;
423 while (snMantissa
>= 10) {
428 if (snExponent
> -5 && snExponent
< precision
) {
429 bool not_null
= false;
430 while (exponent
< 0) {
431 if ((not_null
== false) && ((mantissa
% 10) != 0)) {
435 sb
.Insert(0, (char)('0' + (mantissa
% 10)));
440 if (sb
.Length
!= 0) {
441 sb
.Insert(0, nfi
.NumberDecimalSeparator
);
447 while (mantissa
> 0) {
448 sb
.Insert(0, (char)('0' + (mantissa
% 10)));
454 bool not_null
= false;
455 while (mantissa
>= 10) {
456 if ((not_null
== false) && ((mantissa
% 10) != 0)) {
460 sb
.Insert (0, (char)('0' + (mantissa
% 10)));
466 sb
.Insert(0, nfi
.NumberDecimalSeparator
);
468 sb
.Insert(0, (char)('0' + (mantissa
% 10)) );
471 sb
.Append("E" + nfi
.PositiveSign
);
474 sb
.Append("E" + nfi
.NegativeSign
);
476 sb
.Append(Math
.Abs(exponent
).ToString("00"));
479 sb
.Insert(0, nfi
.NegativeSign
);
482 return sb
.ToString();
485 private string FormatNumber (Format formatData
, double value, NumberFormatInfo nfi
, int precision
) {
487 precision
= (precision
>= 0) ? precision
: nfi
.NumberDecimalDigits
;
488 string numb
= FormatNumberInternal (formatData
, value, nfi
, precision
);
490 switch (nfi
.NumberNegativePattern
) {
492 return "(" + numb
+ ")";
494 return nfi
.NegativeSign
+ numb
;
496 return nfi
.NegativeSign
+ " " + numb
;
498 return numb
+ nfi
.NegativeSign
;
500 return numb
+ " " + nfi
.NegativeSign
;
502 throw new ArgumentException(Locale
.GetText(
503 "Invalid NumberNegativePattern"));
509 private string FormatPercent (Format formatData
, double value, NumberFormatInfo nfi
,
512 precision
= (precision
>= 0) ? precision
: nfi
.PercentDecimalDigits
;
513 string numb
= FormatNumberInternal (formatData
, value*100, nfi
, precision
);
515 switch (nfi
.PercentNegativePattern
) {
517 return nfi
.NegativeSign
+ numb
+ " " + nfi
.PercentSymbol
;
519 return nfi
.NegativeSign
+ numb
+ nfi
.PercentSymbol
;
521 return nfi
.NegativeSign
+ nfi
.PercentSymbol
+ numb
;
523 throw new ArgumentException(Locale
.GetText(
524 "Invalid PercentNegativePattern"));
528 switch (nfi
.PercentPositivePattern
) {
530 return numb
+ " " + nfi
.PercentSymbol
;
532 return numb
+ nfi
.PercentSymbol
;
534 return nfi
.PercentSymbol
+ numb
;
536 throw new ArgumentException(Locale
.GetText(
537 "invalid PercehtPositivePattern"));
542 private string FormatNumberInternal (Format formatData
, double value, NumberFormatInfo nfi
, int precision
)
544 StringBuilder sb
= new StringBuilder();
545 int decimals
= precision
;
548 Normalize (formatData
, value, precision
, out mantissa
, out exponent
);
550 while (decimals
> 0) {
556 int decimal_limit
= -(decimals
+ 1);
557 while (exponent
< 0) {
558 if (exponent
> decimal_limit
) {
559 sb
.Insert(0, (char)('0' + (mantissa
% 10)));
566 sb
.Append ('0', decimals
);
570 if (precision
!= 0) {
571 sb
.Insert(0, nfi
.NumberDecimalSeparator
);
579 int groupSize
= nfi
.NumberGroupSizes
[0];
580 if (groupSize
== 0) groupSize
= int.MaxValue
;
582 while (exponent
> 0 || mantissa
!= 0) {
584 if (groupPos
== groupSize
) {
585 sb
.Insert (0, nfi
.NumberGroupSeparator
);
587 if (groupIndex
< nfi
.NumberGroupSizes
.Length
- 1) {
589 groupSize
= nfi
.NumberGroupSizes
[groupIndex
];
590 if (groupSize
== 0) groupSize
= int.MaxValue
;
599 sb
.Insert(0, (char)('0' + (mantissa
% 10)));
606 return sb
.ToString();
609 // from http://www.yoda.arachsys.com/csharp/floatingpoint.html
610 // used with permission from original author
611 private string FormatReversible (double value, NumberFormatInfo nfi
, int precision
)
613 // Translate the double into sign, exponent and mantissa.
614 long bits
= BitConverter
.DoubleToInt64Bits (value);
615 bool negative
= ((bits
>> 63) == -1);
616 int exponent
= (int) ((bits
>> 52) & 0x7ffL
);
617 long mantissa
= bits
& 0xfffffffffffffL
;
619 // Subnormal numbers; exponent is effectively one higher,
620 // but there's no extra normalisation bit in the mantissa
624 // Normal numbers; leave exponent as it is but add extra
625 // bit to the front of the mantissa
627 mantissa
= mantissa
| (1L<<52);
630 // Bias the exponent. It's actually biased by 1023, but we're
631 // treating the mantissa as m.0 rather than 0.m, so we need
632 // to subtract another 52 from it.
640 while((mantissa
& 1) == 0) {
641 // i.e., Mantissa is even
646 // Construct a new decimal expansion with the mantissa
647 ArbitraryDecimal ad
= new ArbitraryDecimal (mantissa
);
649 // If the exponent is less than 0, we need to repeatedly
650 // divide by 2 - which is the equivalent of multiplying
651 // by 5 and dividing by 10.
653 for (int i
=0; i
< -exponent
; i
++)
655 ad
.Shift (-exponent
);
657 // Otherwise, we need to repeatedly multiply by 2
659 for (int i
=0; i
< exponent
; i
++)
663 // Finally, return the string with an appropriate sign
665 return nfi
.NegativeSign
+ ad
.ToString (nfi
);
667 return ad
.ToString (nfi
);
670 private string FormatCustom (Format formatData
, double value,
671 NumberFormatInfo nfi
, string format
) {
672 int first_semicolon
, second_semicolon
, third_semicolon
;
673 first_semicolon
= format
.IndexOf(';');
674 second_semicolon
= format
.IndexOf(';', first_semicolon
+ 1);
675 if (second_semicolon
< 0) {
676 if (first_semicolon
== -1) {
678 string result
= FormatCustomParser (formatData
, value, nfi
, format
);
682 if (result
.Length
> 0) {
683 result
= nfi
.NegativeSign
+ result
;
687 return FormatCustomParser (formatData
, value, nfi
, format
);
691 return FormatCustomParser
692 (formatData
, value, nfi
, format
.Substring(first_semicolon
+ 1));
694 return FormatCustomParser (formatData
, value, nfi
,
695 format
.Substring(0, first_semicolon
- 1));
698 return FormatCustomParser (formatData
, value, nfi
,
699 format
.Substring(0, first_semicolon
- 1));
701 else if (value < 0.0) {
702 return FormatCustomParser (formatData
, value, nfi
,
703 format
.Substring (first_semicolon
+ 1,
704 second_semicolon
- first_semicolon
- 1));
706 third_semicolon
= second_semicolon
< 0 ? - 1 : format
.IndexOf (';', second_semicolon
+ 1);
707 if (third_semicolon
< 0)
708 return FormatCustomParser (formatData
, value,
709 nfi
, format
.Substring(second_semicolon
+ 1));
711 return FormatCustomParser (formatData
, value,
712 nfi
, format
.Substring(second_semicolon
+ 1, third_semicolon
- second_semicolon
- 1));
715 private struct Flags
{
716 public int NumberOfColons
;
717 public bool Groupping
;
719 public bool Permille
;
722 public int FirstFormatPos
;
723 public int IntegralLength
;
724 public int DecimalLength
;
725 public int ExponentialLength
;
728 private Flags
AnalizeFormat (string format
) {
729 Flags f
= new Flags();
730 f
.NumberOfColons
= 0;
735 f
.FirstFormatPos
= -1;
736 int aux
= 0, i
= 0, count
= 0;
737 bool inQuote
= false;
738 foreach (char c
in format
) {
746 } else if (inQuote
) {
757 if (f
.FirstFormatPos
< 0) {
758 f
.FirstFormatPos
= i
;
771 f
.IntegralLength
= count
;
774 f
.NumberOfColons
= aux
;
786 f
.DecimalLength
= count
;
794 throw new FormatException ("Literal in format string is not correctly terminated.");
796 f
.NumberOfColons
= aux
;
798 if (f
.DecimalLength
> 0) {
799 f
.ExponentialLength
= count
;
802 f
.DecimalLength
= count
;
807 private string FormatCustomParser (Format formatData
, double value,
808 NumberFormatInfo nfi
, string format
) {
811 Flags f
= AnalizeFormat(format
);
812 if (f
.FirstFormatPos
< 0) {
815 if (((f
.Percent
) || (f
.Permille
) || (f
.NumberOfColons
> 0)) && (f
.ExpPos
< 0)) {
816 int len
= f
.DecimalLength
;
822 else if (f
.Permille
) {
826 if (f
.NumberOfColons
> 0) {
827 len
-= (3 * f
.NumberOfColons
);
828 exp
-= 3 * f
.NumberOfColons
;
833 value = Round(value, len
);
834 Normalize (formatData
, value, 15, out mantissa
, out exponent
);
838 value = Round(value, f
.DecimalLength
);
839 Normalize (formatData
, value, 15, out mantissa
, out exponent
);
841 StringBuilder sb
= new StringBuilder();
843 StringBuilder sb_decimal
= new StringBuilder();
844 while (mantissa
> 0) {
845 sb_decimal
.Insert(0, (char)('0' + (mantissa
% 10)));
851 for (k
= sb_decimal
.Length
- 1;
852 k
>= 0 && sb_decimal
[k
] == '0'; k
--);
853 sb_decimal
.Remove(k
+ 1, sb_decimal
.Length
- k
- 1);
854 for (int i
= f
.DotPos
- 2; i
>= 0; i
--) {
856 if (i
> 0 && format
[i
-1] == '\\') {
873 sb
.Append(sb_decimal
[0]);
874 sb
.Append(nfi
.NumberDecimalSeparator
);
875 for (int j
= 1, i
= f
.DotPos
+ 1; i
< f
.ExpPos
; i
++) {
879 sb
.Append(format
[++i
]);
882 if (j
>= sb_decimal
.Length
) {
888 if (j
< sb_decimal
.Length
) {
889 if ((i
== f
.ExpPos
- 1) &&
890 (j
< sb_decimal
.Length
- 1)) {
891 int a
= sb_decimal
[j
] - '0';
892 int b
= sb_decimal
[j
+1] - '0';
893 if (((b
== 5) && ((a
% 2) == 0)) || (b
> 5)) {
896 sb
.Append((char)('0' + (a
% 10)));
899 sb
.Append(sb_decimal
[j
++]);
908 sb
.Append(format
[f
.ExpPos
]);
913 inicio
= f
.ExpPos
+ 1;
914 if (format
[inicio
] == '-') {
917 else if (format
[inicio
] == '+') {
924 while (fin
< format
.Length
&& format
[fin
++] == '0');
925 StringBuilder sb_exponent
= new StringBuilder();
926 exponent
= Math
.Abs(exponent
);
927 while (exponent
> 0) {
928 sb_exponent
.Insert(0, (char)('0' + (exponent
% 10)));
931 while (sb_exponent
.Length
< (fin
- inicio
)) {
932 sb_exponent
.Insert(0, '0');
934 sb
.Append(sb_exponent
.ToString());
935 for (int i
= fin
; i
< format
.Length
; i
++) {
936 sb
.Append(format
[i
]);
938 return sb
.ToString();
941 f
.ExpPos
= format
.Length
;
944 while (exponent
< 0) {
945 mantissa
= (long) Round((double)mantissa
/ 10);
948 f
.DotPos
= format
.Length
;
951 StringBuilder sb_decimal
= new StringBuilder();
952 while (exponent
< 0) {
953 sb_decimal
.Insert(0, (char)('0' + (mantissa
% 10)));
958 for (k
= sb_decimal
.Length
- 1;
959 k
>= 0 && sb_decimal
[k
] == '0'; k
--);
960 sb_decimal
.Remove(k
+ 1, sb_decimal
.Length
- k
- 1);
961 if (sb_decimal
.Length
> 0) {
962 sb
.Append(nfi
.NumberDecimalSeparator
);
964 else if (format
[f
.DotPos
+ 1] == '0') {
965 sb
.Append(nfi
.NumberDecimalSeparator
);
967 bool terminado
= false;
968 for (int j
= 0, i
= f
.DotPos
+ 1; i
< f
.ExpPos
; i
++) {
969 if (format
[i
] == '0' || format
[i
] == '#') {
970 if (j
< sb_decimal
.Length
) {
971 sb
.Append(sb_decimal
[j
++]);
973 else if (format
[i
] == '0' && !terminado
) {
976 else if (format
[i
] == '#' && !terminado
) {
980 else if (format
[i
] == '\\') {
981 sb
.Append(format
[++i
]);
983 else if (format
[i
] == '%')
984 sb
.Append (nfi
.PercentSymbol
);
985 else if (format
[i
] == '\u2030')
986 sb
.Append (nfi
.PerMilleSymbol
);
987 else if (format
[i
] == '\'') {
989 while (i
< format
.Length
) {
990 if (format
[i
] == '\'')
994 sb
.Insert (0, format
.Substring (l
, i
- l
));
997 sb
.Append(format
[i
]);
1002 for (int i
= f
.DotPos
- 1; i
>= f
.FirstFormatPos
; i
--) {
1003 if (format
[i
] == '#' || format
[i
] == '0') {
1004 if (exponent
> 0 || mantissa
> 0 || format
[i
] == '0') {
1005 if (f
.Groupping
&& gro
== nfi
.NumberGroupSizes
[0]) {
1006 sb
.Insert(0, nfi
.NumberGroupSeparator
);
1014 else if (mantissa
> 0) {
1015 sb
.Insert(0, (char)('0' + (mantissa
% 10)));
1018 else if (format
[i
] == '0') {
1023 else if (format
[i
] == '%')
1024 sb
.Insert (0, nfi
.PercentSymbol
);
1025 else if (format
[i
] == '\u2030')
1026 sb
.Insert (0, nfi
.PerMilleSymbol
);
1027 else if (format
[i
] == '\'') {
1030 if (format
[i
] == '\'')
1034 sb
.Insert (0, format
.Substring (i
, l
- i
));
1036 else if (format
[i
] != ',') {
1037 sb
.Insert(0, format
[i
]);
1039 else if (i
> 0 && format
[i
-1] == '\\') {
1040 sb
.Insert(0, format
[i
]);
1044 while (exponent
> 0) {
1045 if (f
.Groupping
&& gro
== nfi
.NumberGroupSizes
[0]) {
1046 sb
.Insert(0, nfi
.NumberGroupSeparator
);
1053 while (mantissa
> 0) {
1054 if (f
.Groupping
&& gro
== nfi
.NumberGroupSizes
[0]) {
1055 sb
.Insert(0, nfi
.NumberGroupSeparator
);
1059 sb
.Insert(0, (char)('0' + (mantissa
% 10)));
1062 for (int i
= f
.FirstFormatPos
- 1; i
>= 0; i
--) {
1063 if (format
[i
] == '%')
1064 sb
.Insert (0, nfi
.PercentSymbol
);
1065 else if (format
[i
] == '\u2030')
1066 sb
.Insert (0, nfi
.PerMilleSymbol
);
1067 else if (format
[i
] == '\'') {
1070 if (format
[i
] == '\'')
1074 sb
.Insert (0, format
.Substring (i
, l
- i
));
1076 else if (format
[i
] != '.')
1077 sb
.Insert(0, format
[i
]);
1079 return sb
.ToString();
1084 // from http://www.yoda.arachsys.com/csharp/floatingpoint.html
1085 // used with permission from original author
1086 internal class ArbitraryDecimal
{
1087 /// <summary>Digits in the decimal expansion, one byte per digit
1090 /// How many digits are *after* the decimal point
1095 /// Constructs an arbitrary decimal expansion from the given long.
1096 /// The long must not be negative.
1098 internal ArbitraryDecimal (long x
)
1100 string tmp
= x
.ToString (CultureInfo
.InvariantCulture
);
1101 digits
= new byte [tmp
.Length
];
1102 for (int i
=0; i
< tmp
.Length
; i
++)
1103 digits
[i
] = (byte) (tmp
[i
] - '0');
1108 /// Multiplies the current expansion by the given amount, which should
1111 internal void MultiplyBy (int amount
)
1113 byte[] result
= new byte [digits
.Length
+1];
1114 for (int i
=digits
.Length
-1; i
>= 0; i
--) {
1115 int resultDigit
= digits
[i
] * amount
+ result
[i
+1];
1116 result
[i
] = (byte)(resultDigit
/ 10);
1117 result
[i
+1] = (byte)(resultDigit
% 10);
1119 if (result
[0] != 0) {
1123 Array
.Copy (result
, 1, digits
, 0, digits
.Length
);
1129 /// Shifts the decimal point; a negative value makes
1130 /// the decimal expansion bigger (as fewer digits come after the
1131 /// decimal place) and a positive value makes the decimal
1132 /// expansion smaller.
1134 internal void Shift (int amount
)
1136 decimalPoint
+= amount
;
1140 /// Removes leading/trailing zeroes from the expansion.
1142 internal void Normalize ()
1145 for (first
=0; first
< digits
.Length
; first
++) {
1146 if (digits
[first
] != 0)
1151 for (last
= digits
.Length
- 1; last
>= 0; last
--) {
1152 if (digits
[last
] != 0)
1156 if ((first
== 0) && (last
== digits
.Length
- 1))
1159 byte[] tmp
= new byte [last
-first
+1];
1160 for (int i
=0; i
< tmp
.Length
; i
++)
1161 tmp
[i
] = digits
[i
+ first
];
1163 decimalPoint
-= digits
.Length
- (last
+ 1);
1168 /// Converts the value to a proper decimal string representation.
1170 public string ToString (NumberFormatInfo nfi
)
1172 char[] digitString
= new char [digits
.Length
];
1173 for (int i
=0; i
< digits
.Length
; i
++)
1174 digitString
[i
] = (char)(digits
[i
] + '0');
1176 // Simplest case - nothing after the decimal point,
1177 // and last real digit is non-zero, eg value=35
1178 if (decimalPoint
== 0) {
1179 return new string (digitString
);
1182 // Fairly simple case - nothing after the decimal
1183 // point, but some 0s to add, eg value=350
1184 if (decimalPoint
< 0) {
1185 return new string (digitString
) + new string ('0', -decimalPoint
);
1188 // Nothing before the decimal point, eg 0.035
1189 if (decimalPoint
>= digitString
.Length
) {
1190 return "0" + nfi
.NumberDecimalSeparator
+
1191 new string ('0',(decimalPoint
-digitString
.Length
))+ new string (digitString
);
1194 // Most complicated case - part of the string comes
1195 // before the decimal point, part comes after it,
1197 return new string (digitString
, 0, digitString
.Length
- decimalPoint
) +
1198 nfi
.NumberDecimalSeparator
+
1199 new string (digitString
, digitString
.Length
- decimalPoint
, decimalPoint
);