2003-03-04 Pedro Mart�nez Juli� <yoros@wanadoo.es>
[mono.git] / mcs / class / corlib / System / DoubleFormatter.cs
1 //
2 // System.DoubleFormatter.cs
3 //
4 // Author:
5 //   Pedro Martinez Juliá  <yoros@wanadoo.es>
6 //
7 // Copyright (C) 2003 Pedro Martínez Juliá <yoros@wanadoo.es>
8 //
9
10 using System;
11 using System.Text;
12 using System.Collections;
13 using System.Globalization;
14
15
16 namespace System {
17
18         internal sealed class DoubleFormatter {
19
20                 public static string NumberToString (string format,
21                                 NumberFormatInfo nfi, double value) {
22                         if (format == null)
23                                 format = "G";
24                         if (nfi == null)
25                                 nfi = NumberFormatInfo.CurrentInfo;
26                         char specifier;
27                         int precision;
28                         if (!DoubleFormatter.ParseFormat(
29                                         format, out specifier, out precision)) {
30                                 throw new FormatException(Locale.GetText(
31                                         "The specified format is invalid"));
32                         }
33                         switch (specifier) {
34                         case 'C':
35                                 return FormatCurrency(nfi, value, precision);
36                         case 'D':
37                                 throw new FormatException(Locale.GetText(
38                                         "The specified format is invalid"));
39                         case 'E':
40                                 return FormatExponential(nfi, value, precision);
41                         case 'F':
42                                 return FormatFixedPoint(nfi, value, precision);
43                         case 'G':
44                                 return FormatGeneral(nfi, value, precision);
45                         case 'N':
46                                 return FormatNumber(nfi, value, precision);
47                         case 'P':
48                                 return FormatPercent(nfi, value, precision);
49                         case 'R':
50                                 return FormatReversible(nfi, value, precision);
51                         case 'X':
52                                 throw new FormatException(Locale.GetText(
53                                         "The specified format is invalid"));
54                         default:
55                                 throw new FormatException(Locale.GetText(
56                                         "The specified format is invalid"));
57                         }
58                 }
59
60                 private static bool ParseFormat (string format,
61                                 out char specifier, out int precision) {
62                         specifier = '\0';
63                         precision = -1;
64                         switch (format.Length) {
65                         case 1:
66                                 specifier = Char.ToUpper(format[0]);
67                                 return true;
68                         case 2:
69                                 if (Char.IsLetter(format[0]) && Char.IsDigit(format[1])) {
70                                         specifier = Char.ToUpper(format[0]);
71                                         precision = Convert.ToInt32(format[1] - '0');
72                                         return true;
73                                 }
74                                 break;
75                         case 3:
76                                 if (Char.IsLetter(format[0]) && Char.IsDigit(format[1])
77                                                 && Char.IsDigit(format[2])) {
78                                         specifier = Char.ToUpper(format[0]);
79                                         precision = Convert.ToInt32(format.Substring(1, 2));
80                                         return true;
81                                 }
82                                 break;
83                         }
84                         return false;
85                 }
86
87                 private static void Normalize (double value, out long mantissa,
88                                 out int exponent) {
89                         mantissa = 0;
90                         exponent = 0;
91                         if (value == 0.0 ||
92                                 Double.IsInfinity(value) ||
93                                 Double.IsNaN(value)) {
94                                 return;
95                         }
96                         value = Math.Abs(value);
97                         double p = 100000000000000.0d;
98                         double p10 = 1000000000000000.0d;
99                         if (value > p10) {
100                                 while (value > p10) {
101                                         value /= 10;
102                                         exponent++;
103                                 }
104                         }
105                         else if (value < p) {
106                                 while (value < p) {
107                                         value *= 10;
108                                         exponent--;
109                                 }
110                         }
111                         mantissa = (long) Math.Round(value);
112                 }
113
114                 private static char[] Digits =
115                         {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
116
117                 private static string FormatCurrency (NumberFormatInfo nfi,
118                                 double value, int precision) {
119                         StringBuilder sb = new StringBuilder();
120                         if (Double.IsNaN(value)) {
121                                 sb.Append(nfi.NaNSymbol);
122                         }
123                         else if (Double.IsInfinity(value)) {
124                                 sb.Append(nfi.PositiveInfinitySymbol);
125                         }
126                         else {
127                                 int decimals = (precision >= 0) ?
128                                         precision : nfi.CurrencyDecimalDigits;
129                                 long mantissa;
130                                 int exponent;
131                                 Normalize(value, out mantissa, out exponent);
132                                 if (exponent >= 0) {
133                                         while (decimals > 0) {
134                                                 sb.Append("0");
135                                                 decimals--;
136                                         }
137                                 }
138                                 else {
139                                         while (exponent < 0) {
140                                                 if (exponent > -(decimals+1)) {
141                                                         sb.Insert(0, Digits[mantissa % 10]);
142                                                 }
143                                                 mantissa /= 10;
144                                                 exponent++;
145                                         }
146                                 }
147                                 sb.Insert(0, nfi.NumberDecimalSeparator);
148                                 if (mantissa == 0) {
149                                         sb.Insert(0, "0");
150                                 }
151                                 else {
152                                         int i = 0;
153                                         while (exponent > 0) {
154                                                 int fin = nfi.NumberGroupSizes[i];
155                                                 for (int j = 0; (j < fin) && (exponent > 0);
156                                                                 j++, exponent--) {
157                                                         sb.Insert(0, "0");
158                                                 }
159                                                 sb.Insert(0, nfi.NumberGroupSeparator);
160                                                 if (i < nfi.NumberGroupSizes.Length - 1)
161                                                         i++;
162                                         }
163                                         while (mantissa != 0) {
164                                                 int fin = nfi.NumberGroupSizes[i];
165                                                 for (int j = 0; (j < fin) && (mantissa != 0); j++) {
166                                                         sb.Insert(0, Digits[mantissa % 10]);
167                                                         mantissa /= 10;
168                                                 }
169                                                 if (mantissa != 0)
170                                                         sb.Insert(0, nfi.NumberGroupSeparator);
171                                                 if (i < nfi.NumberGroupSizes.Length - 1)
172                                                         i++;
173                                         }
174                                 }
175                         }
176                         string numb = sb.ToString();
177                         if (value < 0) {
178                                 switch (nfi.CurrencyNegativePattern) {
179                                 case 0:
180                                         return "(" + nfi.CurrencySymbol + numb + ")";
181                                 case 1:
182                                         return nfi.NegativeSign + nfi.CurrencySymbol + numb;
183                                 case 2:
184                                         return nfi.CurrencySymbol + nfi.NegativeSign + numb;
185                                 case 3:
186                                         return nfi.CurrencySymbol + numb + nfi.NegativeSign;
187                                 case 4:
188                                         return "(" + numb + nfi.CurrencySymbol + ")";
189                                 case 5:
190                                         return nfi.NegativeSign + numb + nfi.CurrencySymbol;
191                                 case 6:
192                                         return numb + nfi.NegativeSign + nfi.CurrencySymbol;
193                                 case 7:
194                                         return numb + nfi.CurrencySymbol + nfi.NegativeSign;
195                                 case 8:
196                                         return nfi.NegativeSign + numb + " " + nfi.CurrencySymbol;
197                                 case 9:
198                                         return nfi.NegativeSign + nfi.CurrencySymbol + " " + numb;
199                                 case 10:
200                                         return numb + " " + nfi.CurrencySymbol + nfi.NegativeSign;
201                                 case 11:
202                                         return nfi.CurrencySymbol + " " + numb + nfi.NegativeSign;
203                                 case 12:
204                                         return nfi.CurrencySymbol + " " + nfi.NegativeSign + numb;
205                                 case 13:
206                                         return numb + nfi.NegativeSign + " " + nfi.CurrencySymbol;
207                                 case 14:
208                                         return "(" + nfi.CurrencySymbol + " " + numb + ")";
209                                 case 15:
210                                         return "(" + numb + " " + nfi.CurrencySymbol + ")";
211                                 default:
212                                         throw new ArgumentException(Locale.GetText(
213                                                 "Invalid CurrencyNegativePattern"));
214                                 }
215                         }
216                         else {
217                                 switch (nfi.CurrencyPositivePattern) {
218                                 case 0:
219                                         return nfi.CurrencySymbol + numb ;
220                                 case 1:
221                                         return numb + nfi.CurrencySymbol;
222                                 case 2:
223                                         return nfi.CurrencySymbol + " " + numb;
224                                 case 3:
225                                         return numb + " " + nfi.CurrencySymbol;
226                                 default:
227                                         throw new ArgumentException(Locale.GetText(
228                                                 "invalid CurrencyPositivePattern"));
229                                 }
230                         }
231                 }
232
233                 [MonoTODO]
234                 private static string FormatExponential (NumberFormatInfo nfi,
235                                 double value, int precision) {
236                         StringBuilder sb = new StringBuilder();
237                         if (value == 0.0) {
238                                 sb.Append("0");
239                         }
240                         else if (Double.IsNaN(value)) {
241                                 sb.Append(nfi.NaNSymbol);
242                         }
243                         else if (Double.IsPositiveInfinity(value)) {
244                                 sb.Append(nfi.PositiveInfinitySymbol);
245                         }
246                         else if (Double.IsNegativeInfinity(value)) {
247                                 sb.Append(nfi.NegativeInfinitySymbol);
248                         }
249                         else {
250                                 int decimals = (precision >= 0) ?
251                                         precision : nfi.NumberDecimalDigits;
252                                 long mantissa;
253                                 int exponent;
254                                 Normalize(value, out mantissa, out exponent);
255                                 bool not_null = false;
256                                 for (int i = 0; i < 14; i++) {
257                                         if ((not_null == false) && ((mantissa % 10) != 0)) {
258                                                 not_null = true;
259                                         }
260                                         if (not_null) {
261                                                 sb.Insert(0,Digits[mantissa % 10]);
262                                         }
263                                         mantissa /= 10;
264                                         exponent++;
265                                 }
266                                 if (sb.Length == 0) {
267                                         sb.Insert(0, "0");
268                                 }
269                                 sb.Insert(0,
270                                         Digits[mantissa % 10] + nfi.NumberDecimalSeparator);
271                                 if (exponent > 0) {
272                                         sb.Append("E" + nfi.PositiveSign);
273                                 }
274                                 else {
275                                         sb.Append("E" + nfi.NegativeSign);
276                                 }
277                                 sb.Append(Math.Abs(exponent).ToString());
278                                 if (value < 0.0) {
279                                         sb.Insert(0, nfi.NegativeSign);
280                                 }
281                         }
282                         return sb.ToString();
283                 }
284
285                 private static string FormatFixedPoint (NumberFormatInfo nfi,
286                                 double value, int precision) {
287                         StringBuilder sb = new StringBuilder();
288                         if (Double.IsNaN(value)) {
289                                 sb.Append(nfi.NaNSymbol);
290                         }
291                         else if (Double.IsPositiveInfinity(value)) {
292                                 sb.Append(nfi.PositiveInfinitySymbol);
293                         }
294                         else if (Double.IsNegativeInfinity(value)) {
295                                 sb.Append(nfi.NegativeInfinitySymbol);
296                         }
297                         else {
298                                 int decimals = (precision >= 0) ?
299                                         precision : nfi.NumberDecimalDigits;
300                                 long mantissa;
301                                 int exponent;
302                                 Normalize(value, out mantissa, out exponent);
303                                 if (exponent >= 0) {
304                                         while (decimals > 0) {
305                                                 sb.Append("0");
306                                                 decimals--;
307                                         }
308                                 }
309                                 else {
310                                         while (exponent < 0) {
311                                                 if (exponent > -(decimals+1)) {
312                                                         sb.Insert(0, Digits[mantissa % 10]);
313                                                 }
314                                                 mantissa /= 10;
315                                                 exponent++;
316                                         }
317                                 }
318                                 sb.Insert(0, nfi.NumberDecimalSeparator);
319                                 if (mantissa == 0) {
320                                         sb.Insert(0, "0");
321                                 }
322                                 else {
323                                         while (exponent > 0) {
324                                                 sb.Insert(0, "0");
325                                                 exponent--;
326                                         }
327                                         while (mantissa != 0) {
328                                                 sb.Insert(0, Digits[mantissa % 10]);
329                                                 mantissa /= 10;
330                                         }
331                                 }
332                                 if (value < 0.0) {
333                                         sb.Insert(0, nfi.NegativeSign);
334                                 }
335                         }
336                         return sb.ToString();
337                 }
338
339                 private static string FormatGeneral (NumberFormatInfo nfi,
340                                 double value, int precision) {
341                         StringBuilder sb = new StringBuilder();
342                         if (value == 0.0) {
343                                 sb.Append("0");
344                         }
345                         else if (Double.IsNaN(value)) {
346                                 sb.Append(nfi.NaNSymbol);
347                         }
348                         else if (Double.IsPositiveInfinity(value)) {
349                                 sb.Append(nfi.PositiveInfinitySymbol);
350                         }
351                         else if (Double.IsNegativeInfinity(value)) {
352                                 sb.Append(nfi.NegativeInfinitySymbol);
353                         }
354                         else {
355                                 int decimals = (precision >= 0) ?
356                                         precision : nfi.NumberDecimalDigits;
357                                 long mantissa;
358                                 int exponent;
359                                 Normalize(value, out mantissa, out exponent);
360                                 if (exponent > -30 && exponent <= 0) {
361                                         bool not_null = false;
362                                         while (exponent < 0) {
363                                                 if ((not_null == false) && ((mantissa % 10) != 0)) {
364                                                         not_null = true;
365                                                 }
366                                                 if (not_null) {
367                                                         sb.Insert(0,Digits[mantissa % 10]);
368                                                 }
369                                                 mantissa /= 10;
370                                                 exponent++;
371                                         }
372                                         if (sb.Length != 0) {
373                                                 sb.Insert(0, nfi.NumberDecimalSeparator);
374                                         }
375                                         if (mantissa == 0) {
376                                                 sb.Insert(0, "0");
377                                         }
378                                         else {
379                                                 while (mantissa > 0) {
380                                                         sb.Insert(0, Digits[mantissa % 10]);
381                                                         mantissa /= 10;
382                                                 }
383                                         }
384                                 }
385                                 else {
386                                         bool not_null = false;
387                                         for (int i = 0; i < 14; i++) {
388                                                 if ((not_null == false) && ((mantissa % 10) != 0)) {
389                                                         not_null = true;
390                                                 }
391                                                 if (not_null) {
392                                                         sb.Insert(0,Digits[mantissa % 10]);
393                                                 }
394                                                 mantissa /= 10;
395                                                 exponent++;
396                                         }
397                                         sb.Insert(0,
398                                                 Digits[mantissa % 10] + nfi.NumberDecimalSeparator);
399                                         if (exponent > 0) {
400                                                 sb.Append("E" + nfi.PositiveSign);
401                                         }
402                                         else {
403                                                 sb.Append("E" + nfi.NegativeSign);
404                                         }
405                                         sb.Append(Math.Abs(exponent).ToString());
406                                 }
407                                 if (value < 0.0) {
408                                         sb.Insert(0, nfi.NegativeSign);
409                                 }
410                         }
411                         return sb.ToString();
412                 }
413
414                 private static string FormatNumber (NumberFormatInfo nfi,
415                                 double value, int precision) {
416                         StringBuilder sb = new StringBuilder();
417                         if (Double.IsNaN(value)) {
418                                 sb.Append(nfi.NaNSymbol);
419                         }
420                         else if (Double.IsInfinity(value)) {
421                                 sb.Append(nfi.PositiveInfinitySymbol);
422                         }
423                         else {
424                                 int decimals = (precision >= 0) ?
425                                         precision : nfi.NumberDecimalDigits;
426                                 long mantissa;
427                                 int exponent;
428                                 Normalize(value, out mantissa, out exponent);
429                                 if (exponent >= 0) {
430                                         while (decimals > 0) {
431                                                 sb.Append("0");
432                                                 decimals--;
433                                         }
434                                 }
435                                 else {
436                                         while (exponent < 0) {
437                                                 if (exponent > -(decimals+1)) {
438                                                         sb.Insert(0, Digits[mantissa % 10]);
439                                                 }
440                                                 mantissa /= 10;
441                                                 exponent++;
442                                         }
443                                 }
444                                 sb.Insert(0, nfi.NumberDecimalSeparator);
445                                 if (mantissa == 0) {
446                                         sb.Insert(0, "0");
447                                 }
448                                 else {
449                                         int i = 0;
450                                         while (exponent > 0) {
451                                                 int fin = nfi.NumberGroupSizes[i];
452                                                 for (int j = 0; (j < fin) && (exponent > 0);
453                                                                 j++, exponent--) {
454                                                         sb.Insert(0, "0");
455                                                 }
456                                                 sb.Insert(0, nfi.NumberGroupSeparator);
457                                                 if (i < nfi.NumberGroupSizes.Length - 1)
458                                                         i++;
459                                         }
460                                         while (mantissa != 0) {
461                                                 int fin = nfi.NumberGroupSizes[i];
462                                                 for (int j = 0; (j < fin) && (mantissa != 0); j++) {
463                                                         sb.Insert(0, Digits[mantissa % 10]);
464                                                         mantissa /= 10;
465                                                 }
466                                                 if (mantissa != 0)
467                                                         sb.Insert(0, nfi.NumberGroupSeparator);
468                                                 if (i < nfi.NumberGroupSizes.Length - 1)
469                                                         i++;
470                                         }
471                                 }
472                         }
473                         string numb = sb.ToString();
474                         if (value < 0) {
475                                 switch (nfi.NumberNegativePattern) {
476                                 case 0:
477                                         return "(" + numb + ")";
478                                 case 1:
479                                         return nfi.NegativeSign + numb;
480                                 case 2:
481                                         return nfi.NegativeSign + " " + numb;
482                                 case 3:
483                                         return numb + nfi.NegativeSign;
484                                 case 4:
485                                         return numb + " " + nfi.NegativeSign;
486                                 default:
487                                         throw new ArgumentException(Locale.GetText(
488                                                 "Invalid NumberNegativePattern"));
489                                 }
490                         }
491                         return numb;
492                 }
493
494                 [MonoTODO]
495                 private static string FormatPercent (NumberFormatInfo nfi,
496                                 double value, int precision) {
497                         StringBuilder sb = new StringBuilder();
498                         if (Double.IsNaN(value)) {
499                                 sb.Append(nfi.NaNSymbol);
500                         }
501                         else if (Double.IsInfinity(value)) {
502                                 sb.Append(nfi.PositiveInfinitySymbol);
503                         }
504                         else {
505                                 int decimals = (precision >= 0) ?
506                                         precision : nfi.PercentDecimalDigits;
507                                 long mantissa;
508                                 int exponent;
509                                 Normalize(value, out mantissa, out exponent);
510                                 exponent += 2;
511                                 if (exponent >= 0) {
512                                         while (decimals > 0) {
513                                                 sb.Append("0");
514                                                 decimals--;
515                                         }
516                                 }
517                                 else {
518                                         while (exponent < 0) {
519                                                 if (exponent > -(decimals+1)) {
520                                                         sb.Insert(0, Digits[mantissa % 10]);
521                                                 }
522                                                 mantissa /= 10;
523                                                 exponent++;
524                                         }
525                                 }
526                                 sb.Insert(0, nfi.NumberDecimalSeparator);
527                                 if (mantissa == 0) {
528                                         sb.Insert(0, "0");
529                                 }
530                                 else {
531                                         int i = 0;
532                                         while (exponent > 0) {
533                                                 int fin = nfi.NumberGroupSizes[i];
534                                                 for (int j = 0; (j < fin) && (exponent > 0);
535                                                                 j++, exponent--) {
536                                                         sb.Insert(0, "0");
537                                                 }
538                                                 sb.Insert(0, nfi.NumberGroupSeparator);
539                                                 if (i < nfi.NumberGroupSizes.Length - 1)
540                                                         i++;
541                                         }
542                                         while (mantissa != 0) {
543                                                 int fin = nfi.NumberGroupSizes[i];
544                                                 for (int j = 0; (j < fin) && (mantissa != 0); j++) {
545                                                         sb.Insert(0, Digits[mantissa % 10]);
546                                                         mantissa /= 10;
547                                                 }
548                                                 if (mantissa != 0)
549                                                         sb.Insert(0, nfi.NumberGroupSeparator);
550                                                 if (i < nfi.NumberGroupSizes.Length - 1)
551                                                         i++;
552                                         }
553                                 }
554                         }
555                         string numb = sb.ToString();
556                         if (value < 0) {
557                                 switch (nfi.PercentNegativePattern) {
558                                 case 0:
559                                         return nfi.NegativeSign + numb + " " + nfi.PercentSymbol;
560                                 case 1:
561                                         return nfi.NegativeSign + numb + nfi.PercentSymbol;
562                                 case 2:
563                                         return nfi.NegativeSign + nfi.PercentSymbol + numb;
564                                 default:
565                                         throw new ArgumentException(Locale.GetText(
566                                                 "Invalid PercentNegativePattern"));
567                                 }
568                         }
569                         else {
570                                 switch (nfi.PercentPositivePattern) {
571                                 case 0:
572                                         return numb + " " + nfi.PercentSymbol;
573                                 case 1:
574                                         return numb + nfi.PercentSymbol;
575                                 case 2:
576                                         return nfi.PercentSymbol + numb;
577                                 default:
578                                         throw new ArgumentException(Locale.GetText(
579                                                 "invalid PercehtPositivePattern"));
580                                 }
581                         }
582                 }
583
584                 [MonoTODO]
585                 private static string FormatReversible (NumberFormatInfo nfi,
586                                 double value, int precision) {
587                         return FormatGeneral(nfi, value, precision);
588                 }
589
590         }
591
592 }