0e56717ad986eb4ebaeb1eab08bfb1c4da3a12f4
[mono.git] / mcs / class / corlib / System / FloatingPointFormatter.cs
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)
12 //
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.
31 //
32
33 using System;
34 using System.Text;
35 using System.Collections;
36 using System.Globalization;
37
38
39 namespace System {
40
41         internal class FloatingPointFormatter {
42
43                 struct Format {
44                         public double p;
45                         public double p10;
46                         public int dec_len;
47                         public int dec_len_min;
48                 }
49                 
50                 Format format1;
51                 Format format2;
52
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) {
56                          
57                         format1.p = p;
58                         format1.p10 = p10;
59                         format1.dec_len = dec_len;
60                         format1.dec_len_min = dec_len_min;
61                         
62                         format2.p = p2;
63                         format2.p10 = p102;
64                         format2.dec_len = dec_len2;
65                         format2.dec_len_min = dec_len_min2;
66                 }
67
68                 public string GetStringFrom
69                                 (string format, NumberFormatInfo nfi, double value) {
70                                 
71                         if (format == null || format == "") {
72                                 format = "G";
73                         }
74                         if (nfi == null) {
75                                 nfi = NumberFormatInfo.CurrentInfo;
76                         }
77                         if (Double.IsNaN(value)) {
78                                 return nfi.NaNSymbol;
79                         }
80                         if (Double.IsNegativeInfinity(value)) {
81                                 return nfi.NegativeInfinitySymbol;
82                         }
83                         if (Double.IsPositiveInfinity(value)) {
84                                 return nfi.PositiveInfinitySymbol;
85                         }
86                         
87                         char specifier;
88                         int precision;
89                         if (!ParseFormat(format, out specifier, out precision)) {
90                                 return FormatCustom (format1, value, nfi, format);
91                         }
92                         
93                         Format formatData = format1;//(precision > format1.dec_len+1) ? format2 : format1;
94                         
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);
120                         }
121                 }
122
123                 private bool ParseFormat (string format,
124                                 out char specifier, out int precision) {
125                         specifier = '\0';
126                         precision = format2.dec_len;
127                         
128                         // FIXME: Math.Round is used and the max is 15.
129                         
130                         if (precision > 15)
131                                 precision = 15;
132                                 
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;
143                                 }
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;
151                                 }
152                                 break;
153                         }
154                         return false;
155                 }
156
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
160
161                 // FIXME: should be moved out of here post Mono 1.0
162                 private double Round (double value) 
163                 {
164                         double int_part = Math.Floor (value);\r
165                         double dec_part = value - int_part;\r
166                         if (dec_part >= 0.5) {\r
167                                 int_part++;\r
168                         }\r
169                         return int_part;\r
170                 }
171                 
172                 // FIXME: should be moved out of here post Mono 1.0
173                 private double Round (double value, int digits) 
174                 {
175                         if (digits == 0)\r
176                                 return Round (value);\r
177                         double p = Math.Pow (10, digits);\r
178                         double int_part = Math.Floor (value);\r
179                         double dec_part = value - int_part;\r
180                         dec_part *= 1000000000000000L;\r
181                         dec_part = Math.Floor (dec_part);\r
182                         dec_part /= (1000000000000000L / p);\r
183                         dec_part = Round (dec_part);\r
184                         dec_part /= p;\r
185                         return int_part + dec_part;\r
186                 }
187
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;
196                         }
197                         value = Math.Abs(value);
198                         if (precision <= (formatData.dec_len) && precision >= 0) {
199                                 value = Round (value, precision);
200                         }
201                         
202                         if (value == 0.0 ||
203                                 Double.IsInfinity(value) ||
204                                 Double.IsNaN(value)) {
205                                 return;
206                         }
207                         
208                         if (value >= formatData.p10) {
209                                 while (value >= formatData.p10) {
210                                         value /= 10;
211                                         exponent++;
212                                 }
213                         }
214                         else if (value < formatData.p) {
215                                 while (value < formatData.p) {
216                                         value *= 10;
217                                         exponent--;
218                                 }
219                         }
220                         mantissa = (long) Round(value);
221                 }
222
223                 private string FormatCurrency (Format formatData, double value,
224                                 NumberFormatInfo nfi, int precision) {
225                                 
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"));
265                                 }
266                         }
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"));
280                                 }
281                         }
282                 }
283
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;
296                                 }
297                                 mantissa = (long) Round(aux);
298                                 for (int i = 0; i < formatData.dec_len - precision; i++) {
299                                         mantissa *= 10;
300                                 }
301                         }
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;
307                                         }
308                                         if (not_null) {
309                                                 sb.Insert(0, (char)('0' + (mantissa % 10)));
310                                                 precision--;
311                                         }
312                                         mantissa /= 10;
313                                         exponent++;
314                                 }
315                         }
316                         if (decimals == 0) {
317                                 sb = new StringBuilder();
318                                 sb.Append((char)('0' + (mantissa % 10)));
319                         }
320                         else {
321                                 while (precision > 0) {
322                                         sb.Append('0');
323                                         precision--;
324                                 }
325                                 if (sb.Length == 0) {
326                                         sb.Insert(0, "0");
327                                 }
328                                 sb.Insert (0, (char)('0' + (mantissa % 10)) +
329                                                 nfi.NumberDecimalSeparator);
330                         }
331                         if (exponent >= 0) {
332                                 sb.Append(exp_char + nfi.PositiveSign);
333                         }
334                         else {
335                                 sb.Append(exp_char + nfi.NegativeSign);
336                         }
337                         sb.Append(Math.Abs(exponent).ToString("000"));
338                         if (value < 0.0) {
339                                 sb.Insert(0, nfi.NegativeSign);
340                         }
341                         return sb.ToString();
342                 }
343
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--;
356                                 }
357                         }
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)));
363                                         }
364                                         mantissa /= 10;
365                                         exponent++;
366                                         decimals--;
367                                 }
368                                 if (decimals > 0) {
369                                         sb.Append ('0', decimals);
370                                         decimals = 0;
371                                 }
372                         }
373                         if (precision != 0) {
374                                 sb.Insert(0, nfi.NumberDecimalSeparator);
375                         }
376                         if (mantissa == 0) {
377                                 sb.Insert(0, "0");
378                         }
379                         else {
380                                 while (exponent > 0) {
381                                         sb.Insert(0, "0");
382                                         exponent--;
383                                 }
384                                 while (mantissa != 0) {
385                                         sb.Insert(0, (char)('0' + (mantissa % 10)));
386                                         mantissa /= 10;
387                                 }
388                         }
389                         if (value < 0.0) {
390                                 sb.Insert(0, nfi.NegativeSign);
391                         }
392                         return sb.ToString();
393                 }
394
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");
400                         }
401                         else {
402                                 precision = (precision > 0) ?
403                                         precision : formatData.dec_len+1;
404                                         
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;
412                                         }
413                                         mantissa = (long) Round (dmant);
414                                         for (int i = 0; i < formatData.dec_len - precision + 1; i++) {
415                                                 mantissa *= 10;
416                                         }
417                                 }
418                                 
419                                 /* Calculate the exponent we would get using the scientific notation */
420                                 int snExponent = exponent;
421                                 long snMantissa = mantissa;
422                         
423                                 while (snMantissa >= 10) {
424                                         snMantissa /= 10;
425                                         snExponent++;
426                                 }
427                                 
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;
433                                                 }
434                                                 if (not_null) {
435                                                         sb.Insert(0, (char)('0' + (mantissa % 10)));
436                                                 }
437                                                 mantissa /= 10;
438                                                 exponent++;
439                                         }
440                                         if (sb.Length != 0) {
441                                                 sb.Insert(0, nfi.NumberDecimalSeparator);
442                                         }
443                                         if (mantissa == 0) {
444                                                 sb.Insert(0, "0");
445                                         }
446                                         else {
447                                                 while (mantissa > 0) {
448                                                         sb.Insert(0, (char)('0' + (mantissa % 10)));
449                                                         mantissa /= 10;
450                                                 }
451                                         }
452                                 }
453                                 else {
454                                         bool not_null = false;
455                                         while (mantissa >= 10) {
456                                                 if ((not_null == false) && ((mantissa % 10) != 0)) {
457                                                         not_null = true;
458                                                 }
459                                                 if (not_null) {
460                                                         sb.Insert (0, (char)('0' + (mantissa % 10)));
461                                                 }
462                                                 mantissa /= 10;
463                                                 exponent++;
464                                         }
465                                         if (sb.Length != 0)
466                                           sb.Insert(0, nfi.NumberDecimalSeparator);
467                                         
468                                         sb.Insert(0, (char)('0' + (mantissa % 10)) );
469
470                                         if (exponent > 0) {
471                                                 sb.Append("E" + nfi.PositiveSign);
472                                         }
473                                         else {
474                                                 sb.Append("E" + nfi.NegativeSign);
475                                         }
476                                         sb.Append(Math.Abs(exponent).ToString("00"));
477                                 }
478                                 if (value < 0.0) {
479                                         sb.Insert(0, nfi.NegativeSign);
480                                 }
481                         }
482                         return sb.ToString();
483                 }
484
485                 private string FormatNumber (Format formatData, double value, NumberFormatInfo nfi, int precision) {
486                 
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"));
504                                 }
505                         }
506                         return numb;
507                 }
508
509                 private string FormatPercent (Format formatData, double value, NumberFormatInfo nfi,
510                                 int precision) {
511
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"));
525                                 }
526                         }
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"));
538                                 }
539                         }
540                 }
541
542                 private string FormatNumberInternal (Format formatData, double value, NumberFormatInfo nfi, int precision) 
543                 {
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--;
553                                 }
554                         }
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)));
560                                         }
561                                         mantissa /= 10;
562                                         exponent++;
563                                         decimals--;
564                                 }
565                                 if (decimals > 0) {
566                                         sb.Append ('0', decimals);
567                                         decimals = 0;
568                                 }
569                         }
570                         if (precision != 0) {
571                                 sb.Insert(0, nfi.NumberDecimalSeparator);
572                         }
573                         if (mantissa == 0) {
574                                 sb.Insert(0, "0");
575                         }
576                         else {
577                                 int groupIndex = 0;
578                                 int groupPos = 0;
579                                 int groupSize = nfi.NumberGroupSizes[0];
580                                 if (groupSize == 0) groupSize = int.MaxValue;
581                                 
582                                 while (exponent > 0 || mantissa != 0) {
583                                         
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;
591                                                 }
592                                         }
593                                         
594                                         if (exponent > 0) {
595                                                 sb.Insert (0, "0");
596                                                 exponent--;
597                                         }
598                                         else {
599                                                 sb.Insert(0, (char)('0' + (mantissa % 10)));
600                                                 mantissa /= 10;
601                                         }
602                                         
603                                         groupPos++;
604                                 }
605                         }
606                         return sb.ToString();
607                 }
608
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)
612                 {
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;
618
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++;
623                         }
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);
628                         }
629         
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;
634         
635                         if (mantissa == 0) {
636                                 return "0";
637                         }
638         
639                         // Normalize
640                         while((mantissa & 1) == 0) {
641                                 //  i.e., Mantissa is even
642                                 mantissa >>= 1;
643                                 exponent++;
644                         }
645         
646                         // Construct a new decimal expansion with the mantissa
647                         ArbitraryDecimal ad = new ArbitraryDecimal (mantissa);
648         
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);
656                         }
657                         // Otherwise, we need to repeatedly multiply by 2
658                         else {
659                                 for (int i=0; i < exponent; i++)
660                                         ad.MultiplyBy(2);
661                         }
662         
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);
668                 }
669
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";
681                                                 }
682                                                 if (result.Length > 0) {
683                                                         result = nfi.NegativeSign + result;
684                                                 }
685                                                 return result;
686                                         }
687                                         return FormatCustomParser (formatData, value, nfi, format);
688                                         
689                                 }
690                                 if (value < 0.0) {
691                                         return FormatCustomParser
692                                                 (formatData, value, nfi, format.Substring(first_semicolon + 1));
693                                 }
694                                 return FormatCustomParser (formatData, value, nfi,
695                                                 format.Substring(0, first_semicolon - 1));
696                         }
697                         if (value > 0.0) {
698                                 return FormatCustomParser (formatData, value, nfi,
699                                                 format.Substring(0, first_semicolon - 1));
700                         }
701                         else if (value < 0.0) {
702                                 return FormatCustomParser (formatData, value, nfi,
703                                                 format.Substring (first_semicolon + 1,
704                                                         second_semicolon - first_semicolon - 1));
705                         }
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));
713                 }
714
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;
726                 }
727
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                         foreach (char c in format) {
738                                 switch (c) {
739                                 case ',':
740                                         aux++;
741                                         break;
742                                 case '0':
743                                 case '#':
744                                         if (f.FirstFormatPos < 0) {
745                                                 f.FirstFormatPos = i;
746                                         }
747                                         if (aux > 0) {
748                                                 f.Groupping = true;
749                                                 aux = 0;
750                                         }
751                                         if (count < 15)
752                                                 count++;
753                                         break;
754                                 case '.':
755                                         if (f.DotPos >= 0)
756                                                 break; // ignore
757                                         f.DotPos = i;
758                                         f.IntegralLength = count;
759                                         count = 0;
760                                         if (aux > 0) {
761                                                 f.NumberOfColons = aux;
762                                                 aux = 0;
763                                         }
764                                         break;
765                                 case '%':
766                                         f.Percent = true;
767                                         break;
768                                 case '\u2030':
769                                         f.Permille = true;
770                                         break;
771                                 case 'e':
772                                 case 'E':
773                                         f.DecimalLength = count;
774                                         count = 0;
775                                         f.ExpPos = i;
776                                         break;
777                                 }
778                                 i++;
779                         }
780                         if (aux > 0) {
781                                 f.NumberOfColons = aux;
782                         }
783                         if (f.DecimalLength > 0) {
784                                 f.ExponentialLength = count;
785                         }
786                         else {
787                                 f.DecimalLength = count;
788                         }
789                         return f;
790                 }
791
792                 private string FormatCustomParser (Format formatData, double value,
793                                 NumberFormatInfo nfi, string format) {
794                         long mantissa;
795                         int exponent;
796                         Flags f = AnalizeFormat(format);
797                         if (f.FirstFormatPos < 0) {
798                                 return format;
799                         }
800                         if (((f.Percent) || (f.Permille) || (f.NumberOfColons > 0)) && (f.ExpPos < 0)) {
801                                 int len = f.DecimalLength;
802                                 int exp = 0;
803                                 if (f.Percent) {
804                                         len += 2;
805                                         exp += 2;
806                                 }
807                                 else if (f.Permille) {
808                                         len += 3;
809                                         exp += 3;
810                                 }
811                                 if (f.NumberOfColons > 0) {
812                                         len -= (3 * f.NumberOfColons);
813                                         exp -= 3 * f.NumberOfColons;
814                                 }
815                                 if (len < 0) {
816                                         len = 0;
817                                 }
818                                 value = Round(value, len);
819                                 Normalize (formatData, value, 15, out mantissa, out exponent);
820                                 exponent += exp;
821                         }
822                         else {
823                                 value = Round(value, f.DecimalLength);
824                                 Normalize (formatData, value, 15, out mantissa, out exponent);
825                         }
826                         StringBuilder sb = new StringBuilder();
827                         if (f.ExpPos > 0) {
828                                 StringBuilder sb_decimal = new StringBuilder();
829                                 while (mantissa > 0) {
830                                         sb_decimal.Insert(0, (char)('0' + (mantissa % 10)));
831                                         mantissa /= 10;
832                                         exponent++;
833                                 }
834                                 exponent--;
835                                 int k;
836                                 for (k = sb_decimal.Length - 1;
837                                         k >= 0 && sb_decimal[k] == '0'; k--);
838                                 sb_decimal.Remove(k + 1, sb_decimal.Length - k - 1);
839                                 for (int i = f.DotPos - 2; i >= 0; i--) {
840                                         char c = format[i];
841                                         if (i > 0 && format[i-1] == '\\') {
842                                                 sb.Insert(0, c);
843                                                 i -= 2;
844                                                 continue;
845                                         }
846                                         switch (c) {
847                                         case ',':
848                                         case '#':
849                                                 break;
850                                         case '0':
851                                                 sb.Insert(0, '0');
852                                                 break;
853                                         default:
854                                                 sb.Insert(0, c);
855                                                 break;
856                                         }
857                                 }
858                                 sb.Append(sb_decimal[0]);
859                                 sb.Append(nfi.NumberDecimalSeparator);
860                                 for (int j = 1, i = f.DotPos + 1; i < f.ExpPos; i++) {
861                                         char c = format[i];
862                                         switch (c) {
863                                         case '\\':
864                                                 sb.Append(format[++i]);
865                                                 break;
866                                         case '0':
867                                                 if (j >= sb_decimal.Length) {
868                                                         sb.Append('0');
869                                                         break;
870                                                 }
871                                                 goto case '#';
872                                         case '#':
873                                                 if (j < sb_decimal.Length) {
874                                                         if ((i == f.ExpPos - 1) &&
875                                                                         (j < sb_decimal.Length - 1)) {
876                                                                 int a = sb_decimal[j] - '0';
877                                                                 int b = sb_decimal[j+1] - '0';
878                                                                 if (((b == 5) && ((a % 2) == 0)) || (b > 5)) {
879                                                                         a++;
880                                                                 }
881                                                                 sb.Append((char)('0' + (a % 10)));
882                                                         }
883                                                         else {
884                                                                 sb.Append(sb_decimal[j++]);
885                                                         }
886                                                 }
887                                                 break;
888                                         default:
889                                                 sb.Append(c);
890                                                 break;
891                                         }
892                                 }
893                                 sb.Append(format[f.ExpPos]);
894                                 if (exponent < 0) {
895                                         sb.Append('-');
896                                 }
897                                 int fin, inicio;
898                                 inicio = f.ExpPos + 1;
899                                 if (format[inicio] == '-') {
900                                         inicio++;
901                                 }
902                                 else if (format[inicio] == '+') {
903                                         if (exponent >= 0) {
904                                                 sb.Append('+');
905                                         }
906                                         inicio++;
907                                 }
908                                 fin = inicio;
909                                 while (fin < format.Length && format[fin++] == '0');
910                                 StringBuilder sb_exponent = new StringBuilder();
911                                 exponent = Math.Abs(exponent);
912                                 while (exponent > 0) {
913                                         sb_exponent.Insert(0, (char)('0' + (exponent % 10)));
914                                         exponent /= 10;
915                                 }
916                                 while (sb_exponent.Length < (fin - inicio)) {
917                                         sb_exponent.Insert(0, '0');
918                                 }
919                                 sb.Append(sb_exponent.ToString());
920                                 for (int i = fin; i < format.Length; i++) {
921                                         sb.Append(format[i]);
922                                 }
923                                 return sb.ToString();
924                         }
925                         else {
926                                 f.ExpPos = format.Length;
927                         }
928                         if (f.DotPos < 0) {
929                                 while (exponent < 0) {
930                                         mantissa = (long) Round((double)mantissa / 10);
931                                         exponent++;
932                                 }
933                                 f.DotPos = format.Length;
934                         }
935                         else {
936                                 StringBuilder sb_decimal = new StringBuilder();
937                                 while (exponent < 0) {
938                                         sb_decimal.Insert(0, (char)('0' + (mantissa % 10)));
939                                         mantissa /= 10;
940                                         exponent++;
941                                 }
942                                 int k;
943                                 for (k = sb_decimal.Length - 1;
944                                         k >= 0 && sb_decimal[k] == '0'; k--);
945                                 sb_decimal.Remove(k + 1, sb_decimal.Length - k - 1);
946                                 if (sb_decimal.Length > 0) {
947                                         sb.Append(nfi.NumberDecimalSeparator);
948                                 }
949                                 else if (format[f.DotPos + 1] == '0') {
950                                         sb.Append(nfi.NumberDecimalSeparator);
951                                 }
952                                 bool terminado = false;
953                                 for (int j = 0, i = f.DotPos + 1; i < f.ExpPos; i++) {
954                                         if (format[i] == '0' || format[i] == '#') {
955                                                 if (j < sb_decimal.Length) {
956                                                         sb.Append(sb_decimal[j++]);
957                                                 }
958                                                 else if (format[i] == '0' && !terminado) {
959                                                         sb.Append('0');
960                                                 }
961                                                 else if (format[i] == '#' && !terminado) {
962                                                         terminado = true;
963                                                 }
964                                         }
965                                         else if (format[i] == '\\') {
966                                                 sb.Append(format[++i]);
967                                         }
968                                         else if (format [i] == '%')
969                                                 sb.Append (nfi.PercentSymbol);
970                                         else if (format [i] == '\u2030')
971                                                 sb.Append (nfi.PerMilleSymbol);
972                                         else {
973                                                 sb.Append(format[i]);
974                                         }
975                                 }
976                         }
977                         int gro = 0;
978                         for (int i = f.DotPos - 1; i >= f.FirstFormatPos; i--) {
979                                 if (format[i] == '#' || format[i] == '0') {
980                                         if (exponent > 0 || mantissa > 0 || format[i] == '0') {
981                                                 if (f.Groupping && gro == nfi.NumberGroupSizes[0]) {
982                                                         sb.Insert(0, nfi.NumberGroupSeparator);
983                                                         gro = 0;
984                                                 }
985                                                 gro++;
986                                                 if (exponent > 0) {
987                                                         sb.Insert(0, '0');
988                                                         exponent--;
989                                                 }
990                                                 else if (mantissa > 0) {
991                                                         sb.Insert(0, (char)('0' + (mantissa % 10)));
992                                                         mantissa /= 10;
993                                                 }
994                                                 else if (format[i] == '0') {
995                                                         sb.Insert(0, '0');
996                                                 }
997                                         }
998                                 }
999                                 else if (format [i] == '%')
1000                                         sb.Insert (0, nfi.PercentSymbol);
1001                                 else if (format [i] == '\u2030')
1002                                         sb.Insert (0, nfi.PerMilleSymbol);
1003                                 else if (format[i] != ',') {
1004                                         sb.Insert(0, format[i]);
1005                                 }
1006                                 else if (i > 0 && format[i-1] == '\\') {
1007                                         sb.Insert(0, format[i]);
1008                                         i -= 2;
1009                                 }
1010                         }
1011                         while (exponent > 0) {
1012                                 if (f.Groupping && gro == nfi.NumberGroupSizes[0]) {
1013                                         sb.Insert(0, nfi.NumberGroupSeparator);
1014                                         gro = 0;
1015                                 }
1016                                 gro++;
1017                                 sb.Insert(0, '0');
1018                                 exponent--;
1019                         }
1020                         while (mantissa > 0) {
1021                                 if (f.Groupping && gro == nfi.NumberGroupSizes[0]) {
1022                                         sb.Insert(0, nfi.NumberGroupSeparator);
1023                                         gro = 0;
1024                                 }
1025                                 gro++;
1026                                 sb.Insert(0, (char)('0' + (mantissa % 10)));
1027                                 mantissa /= 10;
1028                         }
1029                         for (int i = f.FirstFormatPos - 1; i >= 0; i--) {
1030                                 if (format [i] == '%')
1031                                         sb.Insert (0, nfi.PercentSymbol);
1032                                 else if (format [i] == '\u2030')
1033                                         sb.Insert (0, nfi.PerMilleSymbol);
1034                                 else if (format [i] != '.')
1035                                         sb.Insert(0, format[i]);
1036                         }
1037                         return sb.ToString();
1038                 }
1039
1040         }
1041
1042         // from http://www.yoda.arachsys.com/csharp/floatingpoint.html
1043         // used with permission from original author
1044         internal class ArbitraryDecimal {
1045                 /// <summary>Digits in the decimal expansion, one byte per digit
1046                 byte[] digits;
1047                 /// <summary> 
1048                 /// How many digits are *after* the decimal point
1049                 /// </summary>
1050                 int decimalPoint=0;
1051
1052                 /// <summary> 
1053                 /// Constructs an arbitrary decimal expansion from the given long.
1054                 /// The long must not be negative.
1055                 /// </summary>
1056                 internal ArbitraryDecimal (long x)
1057                 {
1058                         string tmp = x.ToString (CultureInfo.InvariantCulture);
1059                         digits = new byte [tmp.Length];
1060                         for (int i=0; i < tmp.Length; i++)
1061                                 digits[i] = (byte) (tmp[i] - '0');
1062                         Normalize ();
1063                 }
1064         
1065                 /// <summary>
1066                 /// Multiplies the current expansion by the given amount, which should
1067                 /// only be 2 or 5.
1068                 /// </summary>
1069                 internal void MultiplyBy (int amount)
1070                 {
1071                         byte[] result = new byte [digits.Length+1];
1072                         for (int i=digits.Length-1; i >= 0; i--) {
1073                                 int resultDigit = digits [i] * amount + result [i+1];
1074                                 result [i] = (byte)(resultDigit / 10);
1075                                 result [i+1] = (byte)(resultDigit % 10);
1076                         }
1077                         if (result [0] != 0) {
1078                                 digits = result;
1079                         }
1080                         else {
1081                                 Array.Copy (result, 1, digits, 0, digits.Length);
1082                         }
1083                         Normalize ();
1084                 }
1085         
1086                 /// <summary>
1087                 /// Shifts the decimal point; a negative value makes
1088                 /// the decimal expansion bigger (as fewer digits come after the
1089                 /// decimal place) and a positive value makes the decimal
1090                 /// expansion smaller.
1091                 /// </summary>
1092                 internal void Shift (int amount)
1093                 {
1094                         decimalPoint += amount;
1095                 }
1096
1097                 /// <summary>
1098                 /// Removes leading/trailing zeroes from the expansion.
1099                 /// </summary>
1100                 internal void Normalize ()
1101                 {
1102                         int first;
1103                         for (first=0; first < digits.Length; first++) {
1104                                 if (digits [first] != 0)
1105                                         break;
1106                         }
1107
1108                         int last;
1109                         for (last = digits.Length - 1; last >= 0; last--) {
1110                                 if (digits [last] != 0)
1111                                         break;
1112                         }
1113             
1114                         if ((first == 0) && (last == digits.Length - 1))
1115                                 return;
1116             
1117                         byte[] tmp = new byte [last-first+1];
1118                         for (int i=0; i < tmp.Length; i++)
1119                                 tmp [i] = digits [i + first];
1120             
1121                         decimalPoint -= digits.Length - (last + 1);
1122                         digits = tmp;
1123                 }
1124
1125                 /// <summary>
1126                 /// Converts the value to a proper decimal string representation.
1127                 /// </summary>
1128                 public string ToString (NumberFormatInfo nfi)
1129                 {
1130                         char[] digitString = new char [digits.Length];            
1131                         for (int i=0; i < digits.Length; i++)
1132                                 digitString [i] = (char)(digits [i] + '0');
1133             
1134                         // Simplest case - nothing after the decimal point,
1135                         // and last real digit is non-zero, eg value=35
1136                         if (decimalPoint == 0) {
1137                                 return new string (digitString);
1138                         }
1139             
1140                         // Fairly simple case - nothing after the decimal
1141                         // point, but some 0s to add, eg value=350
1142                         if (decimalPoint < 0) {
1143                                 return new string (digitString) + new string ('0', -decimalPoint);
1144                         }
1145             
1146                         // Nothing before the decimal point, eg 0.035
1147                         if (decimalPoint >= digitString.Length) {
1148                                 return "0" + nfi.NumberDecimalSeparator + 
1149                                         new string ('0',(decimalPoint-digitString.Length))+ new string (digitString);
1150                         }
1151
1152                         // Most complicated case - part of the string comes
1153                         // before the decimal point, part comes after it,
1154                         // eg 3.5
1155                         return new string (digitString, 0, digitString.Length - decimalPoint) +
1156                                 nfi.NumberDecimalSeparator + 
1157                                 new string (digitString, digitString.Length - decimalPoint, decimalPoint);
1158                 }
1159         }
1160 }