2 // System.DoubleFormatter.cs
5 // Pedro Martinez Juliá <yoros@wanadoo.es>
7 // Copyright (C) 2003 Pedro Martínez Juliá <yoros@wanadoo.es>
12 using System.Collections;
13 using System.Globalization;
18 internal sealed class DoubleFormatter {
20 public static string NumberToString (string format,
21 NumberFormatInfo nfi, double value) {
25 nfi = NumberFormatInfo.CurrentInfo;
28 if (!DoubleFormatter.ParseFormat(
29 format, out specifier, out precision)) {
30 throw new FormatException(Locale.GetText(
31 "The specified format is invalid"));
35 return FormatCurrency(nfi, value, precision);
37 throw new FormatException(Locale.GetText(
38 "The specified format is invalid"));
40 return FormatExponential(nfi, value, precision);
42 return FormatFixedPoint(nfi, value, precision);
44 return FormatGeneral(nfi, value, precision);
46 return FormatNumber(nfi, value, precision);
48 return FormatPercent(nfi, value, precision);
50 return FormatReversible(nfi, value, precision);
52 throw new FormatException(Locale.GetText(
53 "The specified format is invalid"));
55 throw new FormatException(Locale.GetText(
56 "The specified format is invalid"));
60 private static bool ParseFormat (string format,
61 out char specifier, out int precision) {
64 switch (format.Length) {
66 specifier = Char.ToUpper(format[0]);
69 if (Char.IsLetter(format[0]) && Char.IsDigit(format[1])) {
70 specifier = Char.ToUpper(format[0]);
71 precision = Convert.ToInt32(format[1] - '0');
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));
87 private static void Normalize (double value, out long mantissa,
92 Double.IsInfinity(value) ||
93 Double.IsNaN(value)) {
96 value = Math.Abs(value);
97 double p = 100000000000000.0d;
98 double p10 = 1000000000000000.0d;
100 while (value > p10) {
105 else if (value < p) {
111 mantissa = (long) Math.Round(value);
114 private static char[] Digits =
115 {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
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);
123 else if (Double.IsInfinity(value)) {
124 sb.Append(nfi.PositiveInfinitySymbol);
127 int decimals = (precision >= 0) ?
128 precision : nfi.CurrencyDecimalDigits;
131 Normalize(value, out mantissa, out exponent);
133 while (decimals > 0) {
139 while (exponent < 0) {
140 if (exponent > -(decimals+1)) {
141 sb.Insert(0, Digits[mantissa % 10]);
147 sb.Insert(0, nfi.NumberDecimalSeparator);
153 while (exponent > 0) {
154 int fin = nfi.NumberGroupSizes[i];
155 for (int j = 0; (j < fin) && (exponent > 0);
159 sb.Insert(0, nfi.NumberGroupSeparator);
160 if (i < nfi.NumberGroupSizes.Length - 1)
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]);
170 sb.Insert(0, nfi.NumberGroupSeparator);
171 if (i < nfi.NumberGroupSizes.Length - 1)
176 string numb = sb.ToString();
178 switch (nfi.CurrencyNegativePattern) {
180 return "(" + nfi.CurrencySymbol + numb + ")";
182 return nfi.NegativeSign + nfi.CurrencySymbol + numb;
184 return nfi.CurrencySymbol + nfi.NegativeSign + numb;
186 return nfi.CurrencySymbol + numb + nfi.NegativeSign;
188 return "(" + numb + nfi.CurrencySymbol + ")";
190 return nfi.NegativeSign + numb + nfi.CurrencySymbol;
192 return numb + nfi.NegativeSign + nfi.CurrencySymbol;
194 return numb + nfi.CurrencySymbol + nfi.NegativeSign;
196 return nfi.NegativeSign + numb + " " + nfi.CurrencySymbol;
198 return nfi.NegativeSign + nfi.CurrencySymbol + " " + numb;
200 return numb + " " + nfi.CurrencySymbol + nfi.NegativeSign;
202 return nfi.CurrencySymbol + " " + numb + nfi.NegativeSign;
204 return nfi.CurrencySymbol + " " + nfi.NegativeSign + numb;
206 return numb + nfi.NegativeSign + " " + nfi.CurrencySymbol;
208 return "(" + nfi.CurrencySymbol + " " + numb + ")";
210 return "(" + numb + " " + nfi.CurrencySymbol + ")";
212 throw new ArgumentException(Locale.GetText(
213 "Invalid CurrencyNegativePattern"));
217 switch (nfi.CurrencyPositivePattern) {
219 return nfi.CurrencySymbol + numb ;
221 return numb + nfi.CurrencySymbol;
223 return nfi.CurrencySymbol + " " + numb;
225 return numb + " " + nfi.CurrencySymbol;
227 throw new ArgumentException(Locale.GetText(
228 "invalid CurrencyPositivePattern"));
234 private static string FormatExponential (NumberFormatInfo nfi,
235 double value, int precision) {
236 StringBuilder sb = new StringBuilder();
240 else if (Double.IsNaN(value)) {
241 sb.Append(nfi.NaNSymbol);
243 else if (Double.IsPositiveInfinity(value)) {
244 sb.Append(nfi.PositiveInfinitySymbol);
246 else if (Double.IsNegativeInfinity(value)) {
247 sb.Append(nfi.NegativeInfinitySymbol);
250 int decimals = (precision >= 0) ?
251 precision : nfi.NumberDecimalDigits;
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)) {
261 sb.Insert(0,Digits[mantissa % 10]);
266 if (sb.Length == 0) {
270 Digits[mantissa % 10] + nfi.NumberDecimalSeparator);
272 sb.Append("E" + nfi.PositiveSign);
275 sb.Append("E" + nfi.NegativeSign);
277 sb.Append(Math.Abs(exponent).ToString());
279 sb.Insert(0, nfi.NegativeSign);
282 return sb.ToString();
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);
291 else if (Double.IsPositiveInfinity(value)) {
292 sb.Append(nfi.PositiveInfinitySymbol);
294 else if (Double.IsNegativeInfinity(value)) {
295 sb.Append(nfi.NegativeInfinitySymbol);
298 int decimals = (precision >= 0) ?
299 precision : nfi.NumberDecimalDigits;
302 Normalize(value, out mantissa, out exponent);
304 while (decimals > 0) {
310 while (exponent < 0) {
311 if (exponent > -(decimals+1)) {
312 sb.Insert(0, Digits[mantissa % 10]);
318 sb.Insert(0, nfi.NumberDecimalSeparator);
323 while (exponent > 0) {
327 while (mantissa != 0) {
328 sb.Insert(0, Digits[mantissa % 10]);
333 sb.Insert(0, nfi.NegativeSign);
336 return sb.ToString();
339 private static string FormatGeneral (NumberFormatInfo nfi,
340 double value, int precision) {
341 StringBuilder sb = new StringBuilder();
345 else if (Double.IsNaN(value)) {
346 sb.Append(nfi.NaNSymbol);
348 else if (Double.IsPositiveInfinity(value)) {
349 sb.Append(nfi.PositiveInfinitySymbol);
351 else if (Double.IsNegativeInfinity(value)) {
352 sb.Append(nfi.NegativeInfinitySymbol);
355 int decimals = (precision >= 0) ?
356 precision : nfi.NumberDecimalDigits;
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)) {
367 sb.Insert(0,Digits[mantissa % 10]);
372 if (sb.Length != 0) {
373 sb.Insert(0, nfi.NumberDecimalSeparator);
379 while (mantissa > 0) {
380 sb.Insert(0, Digits[mantissa % 10]);
386 bool not_null = false;
387 for (int i = 0; i < 14; i++) {
388 if ((not_null == false) && ((mantissa % 10) != 0)) {
392 sb.Insert(0,Digits[mantissa % 10]);
398 Digits[mantissa % 10] + nfi.NumberDecimalSeparator);
400 sb.Append("E" + nfi.PositiveSign);
403 sb.Append("E" + nfi.NegativeSign);
405 sb.Append(Math.Abs(exponent).ToString());
408 sb.Insert(0, nfi.NegativeSign);
411 return sb.ToString();
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);
420 else if (Double.IsInfinity(value)) {
421 sb.Append(nfi.PositiveInfinitySymbol);
424 int decimals = (precision >= 0) ?
425 precision : nfi.NumberDecimalDigits;
428 Normalize(value, out mantissa, out exponent);
430 while (decimals > 0) {
436 while (exponent < 0) {
437 if (exponent > -(decimals+1)) {
438 sb.Insert(0, Digits[mantissa % 10]);
444 sb.Insert(0, nfi.NumberDecimalSeparator);
450 while (exponent > 0) {
451 int fin = nfi.NumberGroupSizes[i];
452 for (int j = 0; (j < fin) && (exponent > 0);
456 sb.Insert(0, nfi.NumberGroupSeparator);
457 if (i < nfi.NumberGroupSizes.Length - 1)
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]);
467 sb.Insert(0, nfi.NumberGroupSeparator);
468 if (i < nfi.NumberGroupSizes.Length - 1)
473 string numb = sb.ToString();
475 switch (nfi.NumberNegativePattern) {
477 return "(" + numb + ")";
479 return nfi.NegativeSign + numb;
481 return nfi.NegativeSign + " " + numb;
483 return numb + nfi.NegativeSign;
485 return numb + " " + nfi.NegativeSign;
487 throw new ArgumentException(Locale.GetText(
488 "Invalid NumberNegativePattern"));
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);
501 else if (Double.IsInfinity(value)) {
502 sb.Append(nfi.PositiveInfinitySymbol);
505 int decimals = (precision >= 0) ?
506 precision : nfi.PercentDecimalDigits;
509 Normalize(value, out mantissa, out exponent);
512 while (decimals > 0) {
518 while (exponent < 0) {
519 if (exponent > -(decimals+1)) {
520 sb.Insert(0, Digits[mantissa % 10]);
526 sb.Insert(0, nfi.NumberDecimalSeparator);
532 while (exponent > 0) {
533 int fin = nfi.NumberGroupSizes[i];
534 for (int j = 0; (j < fin) && (exponent > 0);
538 sb.Insert(0, nfi.NumberGroupSeparator);
539 if (i < nfi.NumberGroupSizes.Length - 1)
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]);
549 sb.Insert(0, nfi.NumberGroupSeparator);
550 if (i < nfi.NumberGroupSizes.Length - 1)
555 string numb = sb.ToString();
557 switch (nfi.PercentNegativePattern) {
559 return nfi.NegativeSign + numb + " " + nfi.PercentSymbol;
561 return nfi.NegativeSign + numb + nfi.PercentSymbol;
563 return nfi.NegativeSign + nfi.PercentSymbol + numb;
565 throw new ArgumentException(Locale.GetText(
566 "Invalid PercentNegativePattern"));
570 switch (nfi.PercentPositivePattern) {
572 return numb + " " + nfi.PercentSymbol;
574 return numb + nfi.PercentSymbol;
576 return nfi.PercentSymbol + numb;
578 throw new ArgumentException(Locale.GetText(
579 "invalid PercehtPositivePattern"));
585 private static string FormatReversible (NumberFormatInfo nfi,
586 double value, int precision) {
587 return FormatGeneral(nfi, value, precision);