**** Merged from MCS ****
[mono-project.git] / mcs / class / corlib / System / FloatingPointFormatter.cs
blob29e337e8e1fcd50fac13356dc7bd4121abda7f92
1 //
2 // System.FloatingPointFormatter.cs
3 //
4 // Authors:
5 // Pedro Martinez Julia <yoros@wanadoo.es>
6 // Jon Skeet <skeet@pobox.com>
7 // Sebastien Pouliot <sebastien@ximian.com>
8 //
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:
20 //
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 //
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.
33 using System;
34 using System.Text;
35 using System.Collections;
36 using System.Globalization;
39 namespace System {
41 internal class FloatingPointFormatter {
43 struct Format {
44 public double p;
45 public double p10;
46 public int dec_len;
47 public int dec_len_min;
50 Format format1;
51 Format format2;
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) {
57 format1.p = p;
58 format1.p10 = p10;
59 format1.dec_len = dec_len;
60 format1.dec_len_min = dec_len_min;
62 format2.p = p2;
63 format2.p10 = p102;
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 == "") {
72 format = "G";
74 if (nfi == null) {
75 nfi = NumberFormatInfo.CurrentInfo;
77 if (Double.IsNaN(value)) {
78 return nfi.NaNSymbol;
80 if (Double.IsNegativeInfinity(value)) {
81 return nfi.NegativeInfinitySymbol;
83 if (Double.IsPositiveInfinity(value)) {
84 return nfi.PositiveInfinitySymbol;
87 char specifier;
88 int precision;
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;
95 switch (specifier) {
96 case 'C':
97 return FormatCurrency (formatData, value, nfi, precision);
98 case 'D':
99 throw new FormatException(Locale.GetText(
100 "The specified format is invalid") + ": " + format);
101 case 'E':
102 formatData = (precision > format1.dec_len) ? format2 : format1;
103 return FormatExponential (formatData, value, nfi, precision, format[0]);
104 case 'F':
105 return FormatFixedPoint (formatData, value, nfi, precision);
106 case 'G':
107 return FormatGeneral (formatData, value, nfi, precision);
108 case 'N':
109 return FormatNumber (formatData, value, nfi, precision);
110 case 'P':
111 return FormatPercent (formatData, value, nfi, precision);
112 case 'R':
113 return FormatReversible (value, nfi, precision);
114 case 'X':
115 throw new FormatException(Locale.GetText(
116 "The specified format is invalid") + ": " + format);
117 default:
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) {
125 specifier = '\0';
126 precision = format2.dec_len;
128 // FIXME: Math.Round is used and the max is 15.
130 if (precision > 15)
131 precision = 15;
133 switch (format.Length) {
134 case 1:
135 specifier = Char.ToUpperInvariant(format[0]);
136 precision = -1;
137 return true;
138 case 2:
139 if (Char.IsLetter(format[0]) && Char.IsDigit(format[1])) {
140 specifier = Char.ToUpperInvariant(format[0]);
141 precision = Convert.ToInt32(format[1] - '0');
142 return true;
144 break;
145 case 3:
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));
150 return true;
152 break;
154 return false;
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) {
167 int_part++;
169 return int_part;
172 // FIXME: should be moved out of here post Mono 1.0
173 private double Round (double value, int digits)
175 if (digits == 0)
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);
184 dec_part /= p;
185 return int_part + dec_part;
188 private void Normalize (Format formatData, double value, int precision,
189 out long mantissa, out int exponent) {
190 mantissa = 0;
191 exponent = 0;
192 if (value == 0.0 ||
193 Double.IsInfinity(value) ||
194 Double.IsNaN(value)) {
195 return;
197 value = Math.Abs(value);
198 if (precision <= (formatData.dec_len) && precision >= 0) {
199 value = Round (value, precision);
202 if (value == 0.0 ||
203 Double.IsInfinity(value) ||
204 Double.IsNaN(value)) {
205 return;
208 if (value >= formatData.p10) {
209 while (value >= formatData.p10) {
210 value /= 10;
211 exponent++;
214 else if (value < formatData.p) {
215 while (value < formatData.p) {
216 value *= 10;
217 exponent--;
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);
228 if (value < 0) {
229 switch (nfi.CurrencyNegativePattern) {
230 case 0:
231 return "(" + nfi.CurrencySymbol + numb + ")";
232 case 1:
233 return nfi.NegativeSign + nfi.CurrencySymbol + numb;
234 case 2:
235 return nfi.CurrencySymbol + nfi.NegativeSign + numb;
236 case 3:
237 return nfi.CurrencySymbol + numb + nfi.NegativeSign;
238 case 4:
239 return "(" + numb + nfi.CurrencySymbol + ")";
240 case 5:
241 return nfi.NegativeSign + numb + nfi.CurrencySymbol;
242 case 6:
243 return numb + nfi.NegativeSign + nfi.CurrencySymbol;
244 case 7:
245 return numb + nfi.CurrencySymbol + nfi.NegativeSign;
246 case 8:
247 return nfi.NegativeSign + numb + " " + nfi.CurrencySymbol;
248 case 9:
249 return nfi.NegativeSign + nfi.CurrencySymbol + " " + numb;
250 case 10:
251 return numb + " " + nfi.CurrencySymbol + nfi.NegativeSign;
252 case 11:
253 return nfi.CurrencySymbol + " " + numb + nfi.NegativeSign;
254 case 12:
255 return nfi.CurrencySymbol + " " + nfi.NegativeSign + numb;
256 case 13:
257 return numb + nfi.NegativeSign + " " + nfi.CurrencySymbol;
258 case 14:
259 return "(" + nfi.CurrencySymbol + " " + numb + ")";
260 case 15:
261 return "(" + numb + " " + nfi.CurrencySymbol + ")";
262 default:
263 throw new ArgumentException(Locale.GetText(
264 "Invalid CurrencyNegativePattern"));
267 else {
268 switch (nfi.CurrencyPositivePattern) {
269 case 0:
270 return nfi.CurrencySymbol + numb ;
271 case 1:
272 return numb + nfi.CurrencySymbol;
273 case 2:
274 return nfi.CurrencySymbol + " " + numb;
275 case 3:
276 return numb + " " + nfi.CurrencySymbol;
277 default:
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;
289 long mantissa;
290 int exponent;
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++) {
295 aux /= 10;
297 mantissa = (long) Round(aux);
298 for (int i = 0; i < formatData.dec_len - precision; i++) {
299 mantissa *= 10;
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)) {
306 not_null = true;
308 if (not_null) {
309 sb.Insert(0, (char)('0' + (mantissa % 10)));
310 precision--;
312 mantissa /= 10;
313 exponent++;
316 if (decimals == 0) {
317 sb = new StringBuilder();
318 sb.Append((char)('0' + (mantissa % 10)));
320 else {
321 while (precision > 0) {
322 sb.Append('0');
323 precision--;
325 if (sb.Length == 0) {
326 sb.Insert(0, "0");
328 sb.Insert (0, (char)('0' + (mantissa % 10)) +
329 nfi.NumberDecimalSeparator);
331 if (exponent >= 0) {
332 sb.Append(exp_char + nfi.PositiveSign);
334 else {
335 sb.Append(exp_char + nfi.NegativeSign);
337 sb.Append(Math.Abs(exponent).ToString("000"));
338 if (value < 0.0) {
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;
349 long mantissa;
350 int exponent;
351 Normalize (formatData, value, precision, out mantissa, out exponent);
352 if (exponent >= 0) {
353 while (decimals > 0) {
354 sb.Append("0");
355 decimals--;
358 else {
359 int decimal_limit = -(decimals + 1);
360 while (exponent < 0) {
361 if (exponent > decimal_limit) {
362 sb.Insert(0, (char)('0' + (mantissa % 10)));
364 mantissa /= 10;
365 exponent++;
366 decimals--;
368 if (decimals > 0) {
369 sb.Append ('0', decimals);
370 decimals = 0;
373 if (precision != 0) {
374 sb.Insert(0, nfi.NumberDecimalSeparator);
376 if (mantissa == 0) {
377 sb.Insert(0, "0");
379 else {
380 while (exponent > 0) {
381 sb.Insert(0, "0");
382 exponent--;
384 while (mantissa != 0) {
385 sb.Insert(0, (char)('0' + (mantissa % 10)));
386 mantissa /= 10;
389 if (value < 0.0) {
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();
398 if (value == 0.0) {
399 sb.Append("0");
401 else {
402 precision = (precision > 0) ?
403 precision : formatData.dec_len+1;
405 long mantissa;
406 int exponent;
407 Normalize (formatData, value, precision, out mantissa, out exponent);
408 if (precision > 0) {
409 double dmant = mantissa;
410 for (int i = 0; i < formatData.dec_len - precision + 1; i++) {
411 dmant /= 10;
413 mantissa = (long) Round (dmant);
414 for (int i = 0; i < formatData.dec_len - precision + 1; i++) {
415 mantissa *= 10;
419 /* Calculate the exponent we would get using the scientific notation */
420 int snExponent = exponent;
421 long snMantissa = mantissa;
423 while (snMantissa >= 10) {
424 snMantissa /= 10;
425 snExponent++;
428 if (snExponent > -5 && snExponent < precision) {
429 bool not_null = false;
430 while (exponent < 0) {
431 if ((not_null == false) && ((mantissa % 10) != 0)) {
432 not_null = true;
434 if (not_null) {
435 sb.Insert(0, (char)('0' + (mantissa % 10)));
437 mantissa /= 10;
438 exponent++;
440 if (sb.Length != 0) {
441 sb.Insert(0, nfi.NumberDecimalSeparator);
443 if (mantissa == 0) {
444 sb.Insert(0, "0");
446 else {
447 while (mantissa > 0) {
448 sb.Insert(0, (char)('0' + (mantissa % 10)));
449 mantissa /= 10;
453 else {
454 bool not_null = false;
455 while (mantissa >= 10) {
456 if ((not_null == false) && ((mantissa % 10) != 0)) {
457 not_null = true;
459 if (not_null) {
460 sb.Insert (0, (char)('0' + (mantissa % 10)));
462 mantissa /= 10;
463 exponent++;
465 if (sb.Length != 0)
466 sb.Insert(0, nfi.NumberDecimalSeparator);
468 sb.Insert(0, (char)('0' + (mantissa % 10)) );
470 if (exponent > 0) {
471 sb.Append("E" + nfi.PositiveSign);
473 else {
474 sb.Append("E" + nfi.NegativeSign);
476 sb.Append(Math.Abs(exponent).ToString("00"));
478 if (value < 0.0) {
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);
489 if (value < 0) {
490 switch (nfi.NumberNegativePattern) {
491 case 0:
492 return "(" + numb + ")";
493 case 1:
494 return nfi.NegativeSign + numb;
495 case 2:
496 return nfi.NegativeSign + " " + numb;
497 case 3:
498 return numb + nfi.NegativeSign;
499 case 4:
500 return numb + " " + nfi.NegativeSign;
501 default:
502 throw new ArgumentException(Locale.GetText(
503 "Invalid NumberNegativePattern"));
506 return numb;
509 private string FormatPercent (Format formatData, double value, NumberFormatInfo nfi,
510 int precision) {
512 precision = (precision >= 0) ? precision : nfi.PercentDecimalDigits;
513 string numb = FormatNumberInternal (formatData, value*100, nfi, precision);
514 if (value < 0) {
515 switch (nfi.PercentNegativePattern) {
516 case 0:
517 return nfi.NegativeSign + numb + " " + nfi.PercentSymbol;
518 case 1:
519 return nfi.NegativeSign + numb + nfi.PercentSymbol;
520 case 2:
521 return nfi.NegativeSign + nfi.PercentSymbol + numb;
522 default:
523 throw new ArgumentException(Locale.GetText(
524 "Invalid PercentNegativePattern"));
527 else {
528 switch (nfi.PercentPositivePattern) {
529 case 0:
530 return numb + " " + nfi.PercentSymbol;
531 case 1:
532 return numb + nfi.PercentSymbol;
533 case 2:
534 return nfi.PercentSymbol + numb;
535 default:
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;
546 long mantissa;
547 int exponent;
548 Normalize (formatData, value, precision, out mantissa, out exponent);
549 if (exponent >= 0) {
550 while (decimals > 0) {
551 sb.Append("0");
552 decimals--;
555 else {
556 int decimal_limit = -(decimals + 1);
557 while (exponent < 0) {
558 if (exponent > decimal_limit) {
559 sb.Insert(0, (char)('0' + (mantissa % 10)));
561 mantissa /= 10;
562 exponent++;
563 decimals--;
565 if (decimals > 0) {
566 sb.Append ('0', decimals);
567 decimals = 0;
570 if (precision != 0) {
571 sb.Insert(0, nfi.NumberDecimalSeparator);
573 if (mantissa == 0) {
574 sb.Insert(0, "0");
576 else {
577 int groupIndex = 0;
578 int groupPos = 0;
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);
586 groupPos = 0;
587 if (groupIndex < nfi.NumberGroupSizes.Length - 1) {
588 groupIndex++;
589 groupSize = nfi.NumberGroupSizes[groupIndex];
590 if (groupSize == 0) groupSize = int.MaxValue;
594 if (exponent > 0) {
595 sb.Insert (0, "0");
596 exponent--;
598 else {
599 sb.Insert(0, (char)('0' + (mantissa % 10)));
600 mantissa /= 10;
603 groupPos++;
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
621 if (exponent == 0) {
622 exponent++;
624 // Normal numbers; leave exponent as it is but add extra
625 // bit to the front of the mantissa
626 else {
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.
633 exponent -= 1075;
635 if (mantissa == 0) {
636 return "0";
639 // Normalize
640 while((mantissa & 1) == 0) {
641 // i.e., Mantissa is even
642 mantissa >>= 1;
643 exponent++;
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.
652 if (exponent < 0) {
653 for (int i=0; i < -exponent; i++)
654 ad.MultiplyBy (5);
655 ad.Shift (-exponent);
657 // Otherwise, we need to repeatedly multiply by 2
658 else {
659 for (int i=0; i < exponent; i++)
660 ad.MultiplyBy(2);
663 // Finally, return the string with an appropriate sign
664 if (negative)
665 return nfi.NegativeSign + ad.ToString (nfi);
666 else
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) {
677 if (value < 0.0) {
678 string result = FormatCustomParser (formatData, value, nfi, format);
679 if (result == "0") {
680 return "0";
682 if (result.Length > 0) {
683 result = nfi.NegativeSign + result;
685 return result;
687 return FormatCustomParser (formatData, value, nfi, format);
690 if (value < 0.0) {
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));
697 if (value > 0.0) {
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));
710 else
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;
718 public bool Percent;
719 public bool Permille;
720 public int DotPos;
721 public int ExpPos;
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;
731 f.DotPos = -1;
732 f.ExpPos = -1;
733 f.Groupping = false;
734 f.Percent = false;
735 f.FirstFormatPos = -1;
736 int aux = 0, i = 0, count = 0;
737 bool inQuote = false;
738 foreach (char c in format) {
739 if (c == '\'') {
740 if (inQuote)
741 inQuote = false;
742 else
743 inQuote = true;
744 i++;
745 continue;
746 } else if (inQuote) {
747 i++;
748 continue;
751 switch (c) {
752 case ',':
753 aux++;
754 break;
755 case '0':
756 case '#':
757 if (f.FirstFormatPos < 0) {
758 f.FirstFormatPos = i;
760 if (aux > 0) {
761 f.Groupping = true;
762 aux = 0;
764 if (count < 15)
765 count++;
766 break;
767 case '.':
768 if (f.DotPos >= 0)
769 break; // ignore
770 f.DotPos = i;
771 f.IntegralLength = count;
772 count = 0;
773 if (aux > 0) {
774 f.NumberOfColons = aux;
775 aux = 0;
777 break;
778 case '%':
779 f.Percent = true;
780 break;
781 case '\u2030':
782 f.Permille = true;
783 break;
784 case 'e':
785 case 'E':
786 f.DecimalLength = count;
787 count = 0;
788 f.ExpPos = i;
789 break;
791 i++;
793 if (inQuote)
794 throw new FormatException ("Literal in format string is not correctly terminated.");
795 if (aux > 0) {
796 f.NumberOfColons = aux;
798 if (f.DecimalLength > 0) {
799 f.ExponentialLength = count;
801 else {
802 f.DecimalLength = count;
804 return f;
807 private string FormatCustomParser (Format formatData, double value,
808 NumberFormatInfo nfi, string format) {
809 long mantissa;
810 int exponent;
811 Flags f = AnalizeFormat(format);
812 if (f.FirstFormatPos < 0) {
813 return format;
815 if (((f.Percent) || (f.Permille) || (f.NumberOfColons > 0)) && (f.ExpPos < 0)) {
816 int len = f.DecimalLength;
817 int exp = 0;
818 if (f.Percent) {
819 len += 2;
820 exp += 2;
822 else if (f.Permille) {
823 len += 3;
824 exp += 3;
826 if (f.NumberOfColons > 0) {
827 len -= (3 * f.NumberOfColons);
828 exp -= 3 * f.NumberOfColons;
830 if (len < 0) {
831 len = 0;
833 value = Round(value, len);
834 Normalize (formatData, value, 15, out mantissa, out exponent);
835 exponent += exp;
837 else {
838 value = Round(value, f.DecimalLength);
839 Normalize (formatData, value, 15, out mantissa, out exponent);
841 StringBuilder sb = new StringBuilder();
842 if (f.ExpPos > 0) {
843 StringBuilder sb_decimal = new StringBuilder();
844 while (mantissa > 0) {
845 sb_decimal.Insert(0, (char)('0' + (mantissa % 10)));
846 mantissa /= 10;
847 exponent++;
849 exponent--;
850 int k;
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--) {
855 char c = format[i];
856 if (i > 0 && format[i-1] == '\\') {
857 sb.Insert(0, c);
858 i -= 2;
859 continue;
861 switch (c) {
862 case ',':
863 case '#':
864 break;
865 case '0':
866 sb.Insert(0, '0');
867 break;
868 default:
869 sb.Insert(0, c);
870 break;
873 sb.Append(sb_decimal[0]);
874 sb.Append(nfi.NumberDecimalSeparator);
875 for (int j = 1, i = f.DotPos + 1; i < f.ExpPos; i++) {
876 char c = format[i];
877 switch (c) {
878 case '\\':
879 sb.Append(format[++i]);
880 break;
881 case '0':
882 if (j >= sb_decimal.Length) {
883 sb.Append('0');
884 break;
886 goto case '#';
887 case '#':
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)) {
894 a++;
896 sb.Append((char)('0' + (a % 10)));
898 else {
899 sb.Append(sb_decimal[j++]);
902 break;
903 default:
904 sb.Append(c);
905 break;
908 sb.Append(format[f.ExpPos]);
909 if (exponent < 0) {
910 sb.Append('-');
912 int fin, inicio;
913 inicio = f.ExpPos + 1;
914 if (format[inicio] == '-') {
915 inicio++;
917 else if (format[inicio] == '+') {
918 if (exponent >= 0) {
919 sb.Append('+');
921 inicio++;
923 fin = 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)));
929 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();
940 else {
941 f.ExpPos = format.Length;
943 if (f.DotPos < 0) {
944 while (exponent < 0) {
945 mantissa = (long) Round((double)mantissa / 10);
946 exponent++;
948 f.DotPos = format.Length;
950 else {
951 StringBuilder sb_decimal = new StringBuilder();
952 while (exponent < 0) {
953 sb_decimal.Insert(0, (char)('0' + (mantissa % 10)));
954 mantissa /= 10;
955 exponent++;
957 int k;
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) {
974 sb.Append('0');
976 else if (format[i] == '#' && !terminado) {
977 terminado = true;
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] == '\'') {
988 int l = ++i;
989 while (i < format.Length) {
990 if (format [i] == '\'')
991 break;
992 i++;
994 sb.Insert (0, format.Substring (l, i - l));
996 else {
997 sb.Append(format[i]);
1001 int gro = 0;
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);
1007 gro = 0;
1009 gro++;
1010 if (exponent > 0) {
1011 sb.Insert(0, '0');
1012 exponent--;
1014 else if (mantissa > 0) {
1015 sb.Insert(0, (char)('0' + (mantissa % 10)));
1016 mantissa /= 10;
1018 else if (format[i] == '0') {
1019 sb.Insert(0, '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] == '\'') {
1028 int l = i;
1029 while (i >= 0) {
1030 if (format [i] == '\'')
1031 break;
1032 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]);
1041 i -= 2;
1044 while (exponent > 0) {
1045 if (f.Groupping && gro == nfi.NumberGroupSizes[0]) {
1046 sb.Insert(0, nfi.NumberGroupSeparator);
1047 gro = 0;
1049 gro++;
1050 sb.Insert(0, '0');
1051 exponent--;
1053 while (mantissa > 0) {
1054 if (f.Groupping && gro == nfi.NumberGroupSizes[0]) {
1055 sb.Insert(0, nfi.NumberGroupSeparator);
1056 gro = 0;
1058 gro++;
1059 sb.Insert(0, (char)('0' + (mantissa % 10)));
1060 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] == '\'') {
1068 int l = i;
1069 while (i >= 0) {
1070 if (format [i] == '\'')
1071 break;
1072 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
1088 byte[] digits;
1089 /// <summary>
1090 /// How many digits are *after* the decimal point
1091 /// </summary>
1092 int decimalPoint=0;
1094 /// <summary>
1095 /// Constructs an arbitrary decimal expansion from the given long.
1096 /// The long must not be negative.
1097 /// </summary>
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');
1104 Normalize ();
1107 /// <summary>
1108 /// Multiplies the current expansion by the given amount, which should
1109 /// only be 2 or 5.
1110 /// </summary>
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) {
1120 digits = result;
1122 else {
1123 Array.Copy (result, 1, digits, 0, digits.Length);
1125 Normalize ();
1128 /// <summary>
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.
1133 /// </summary>
1134 internal void Shift (int amount)
1136 decimalPoint += amount;
1139 /// <summary>
1140 /// Removes leading/trailing zeroes from the expansion.
1141 /// </summary>
1142 internal void Normalize ()
1144 int first;
1145 for (first=0; first < digits.Length; first++) {
1146 if (digits [first] != 0)
1147 break;
1150 int last;
1151 for (last = digits.Length - 1; last >= 0; last--) {
1152 if (digits [last] != 0)
1153 break;
1156 if ((first == 0) && (last == digits.Length - 1))
1157 return;
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);
1164 digits = tmp;
1167 /// <summary>
1168 /// Converts the value to a proper decimal string representation.
1169 /// </summary>
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,
1196 // eg 3.5
1197 return new string (digitString, 0, digitString.Length - decimalPoint) +
1198 nfi.NumberDecimalSeparator +
1199 new string (digitString, digitString.Length - decimalPoint, decimalPoint);