2 // System.DecimalFormatter.cs
5 // Martin Weindel (martin.weindel@t-online.de)
7 // (C) Martin Weindel, Derek Holden dholden@draper.com
11 // Internal class for formatting decimal numbers.
13 using System.Globalization;
20 internal sealed class DecimalFormatter
\r
23 private static bool ParseFormat (string format, out char specifier, out int precision)
28 int length = format.Length;
29 if (length < 1 || length > 3)
32 char[] chars = format.ToCharArray ();
33 specifier = Char.ToUpper(chars[0]);
40 if (chars[1] < '0' || chars[1] > '9')
43 precision = chars[1] - '0';
47 if (chars[1] < '0' || chars[2] < '0' || chars[1] > '9' || chars[2] > '9')
50 precision = (chars[1] - '0') * 10 + (chars[2] - '0');
56 public static string NumberToString(string format, NumberFormatInfo nfi, Decimal value)
\r
60 if (!DecimalFormatter.ParseFormat(format, out specifier, out precision))
\r
62 throw new FormatException (Locale.GetText ("The specified format is invalid"));
\r
67 // first calculate number of digits or decimals needed for format
\r
71 decimals = (precision >= 0) ? precision : nfi.CurrencyDecimalDigits;
\r
73 case 'F': goto case 'N';
\r
75 decimals = (precision >= 0) ? precision : nfi.NumberDecimalDigits;
\r
78 digits = (precision >= 0) ? precision : 0;
\r
81 digits = (precision >= 0) ? precision+1 : 7;
\r
84 decimals = (precision >= 0) ? precision+2 : nfi.PercentDecimalDigits+2;
\r
92 const int bufSize = 40;
\r
93 int decPos = 0, sign = 0;
\r
94 char[] buf = new char[bufSize];
\r
95 if (Decimal.decimal2string(ref value, digits, decimals, buf, bufSize, out decPos, out sign) != 0)
\r
97 throw new FormatException(); // should never happen
\r
100 string TempString = new String(buf);
101 TempString = TempString.Trim(new char[] {(char)0x0});
102 StringBuilder sb = new StringBuilder(TempString, TempString.Length);
\r
104 if (sb.ToString () == String.Empty && decPos > 0 && sign == 0)
107 // now build the format
\r
110 case 'C': return FormatCurrency(nfi, sb, decimals, decPos, sign);
\r
111 case 'N': return FormatNumber(nfi, sb, decimals, decPos, sign);
\r
112 case 'F': return FormatFixedPoint(nfi, sb, decimals, decPos, sign);
\r
113 case 'G': return FormatGeneral(nfi, sb, digits, decPos, sign, format[0]);
\r
114 case 'E': return FormatExponential(nfi, sb, digits, decPos, sign, format[0], true);
\r
115 case 'P': return FormatPercent(nfi, sb, decimals, decPos, sign);
\r
116 case 'Z': return FormatNormalized(nfi, sb, digits, decPos, sign);
\r
118 throw new FormatException (Locale.GetText ("The specified format is invalid"));
\r
122 private static string FormatFixedPoint(NumberFormatInfo nfi, StringBuilder sb,
123 int decimals, int decPos, int sign)
127 sb.Insert((decPos <= 0) ? 1 : decPos, nfi.NumberDecimalSeparator);
132 sb.Insert(0, nfi.NegativeSign);
135 return sb.ToString();
138 private static string FormatExponential(NumberFormatInfo nfi, StringBuilder sb,
139 int digits, int decPos, int sign, char echar, bool exp3flag)
141 // insert decimal separator
142 if (digits > 1 || (digits == 0 && sb.Length > 1))
\r
144 sb.Insert(1, nfi.NumberDecimalSeparator);
150 sb.Insert(0, nfi.NegativeSign);
156 sb.Append((decPos >= 0) ? nfi.PositiveSign : nfi.NegativeSign);
157 if (decPos < 0) decPos *= -1;
158 if (exp3flag) sb.Append('0');
159 sb.Append((char)('0' + decPos/10));
160 sb.Append((char)('0' + decPos%10));
162 return sb.ToString();
165 private static string FormatGeneral(NumberFormatInfo nfi, StringBuilder sb,
166 int digits, int decPos, int sign, char gchar)
169 bool bFix = (digits == 0 && decPos >= -3) || (digits >= decPos && decPos >= -3 && digits != 0);
171 // remove trailing digits
172 while (sb.Length > 1 && (sb.Length > decPos || !bFix) && sb[sb.Length-1] == '0')
174 sb.Remove(sb.Length-1, 1);
180 while (decPos <= 0)
\r
183 if (dig != 0 && decPos != 0) dig++;
186 return FormatFixedPoint(nfi, sb, sb.Length - decPos, decPos, sign);
190 return FormatExponential(nfi, sb, dig, decPos, sign, (char)(gchar-2), false);
194 private static string FormatGroupAndDec(StringBuilder sb, int decimals, int decPos,
195 int[] groupSizes, string groupSeparator, string decSeparator)
202 if (groupSizes != null)
\r
206 for (int i = 0; i < groupSizes.GetLength(0); i++)
\r
208 int size = groupSizes[i];
212 if (digitCount < decPos)
\r
214 sb.Insert(decPos - digitCount, groupSeparator);
215 offset += groupSeparator.Length;
225 digitCount +=lastSize;
226 if (digitCount >= decPos) break;
227 sb.Insert(decPos - digitCount, groupSeparator);
228 offset += groupSeparator.Length;
236 sb.Insert(offset + ((decPos <= 0) ? 1 : decPos), decSeparator);
239 return sb.ToString();
242 private static string FormatNumber(NumberFormatInfo nfi, StringBuilder sb,
243 int decimals, int decPos, int sign)
245 string s = FormatGroupAndDec(sb, decimals, decPos,
246 nfi.NumberGroupSizes, nfi.NumberGroupSeparator, nfi.NumberDecimalSeparator);
251 switch (nfi.NumberNegativePattern)
\r
254 return "(" + s + ")";
256 return nfi.NegativeSign + s;
258 return nfi.NegativeSign + " " + s;
260 return s + nfi.NegativeSign;
262 return s + " " + nfi.NegativeSign;
264 throw new ArgumentException(Locale.GetText ("Invalid NumberNegativePattern"));
273 private static string FormatCurrency(NumberFormatInfo nfi, StringBuilder sb,
274 int decimals, int decPos, int sign)
276 string s = FormatGroupAndDec(sb, decimals, decPos,
277 nfi.CurrencyGroupSizes, nfi.CurrencyGroupSeparator, nfi.CurrencyDecimalSeparator);
281 switch (nfi.CurrencyNegativePattern)
\r
284 return "(" + nfi.CurrencySymbol + s + ")";
286 return nfi.NegativeSign + nfi.CurrencySymbol + s;
288 return nfi.CurrencySymbol + nfi.NegativeSign + s;
290 return nfi.CurrencySymbol + s + nfi.NegativeSign;
292 return "(" + s + nfi.CurrencySymbol + ")";
294 return nfi.NegativeSign + s + nfi.CurrencySymbol;
296 return s + nfi.NegativeSign + nfi.CurrencySymbol;
298 return s + nfi.CurrencySymbol + nfi.NegativeSign;
300 return nfi.NegativeSign + s + " " + nfi.CurrencySymbol;
302 return nfi.NegativeSign + nfi.CurrencySymbol + " " + s;
304 return s + " " + nfi.CurrencySymbol + nfi.NegativeSign;
306 return nfi.CurrencySymbol + " " + s + nfi.NegativeSign;
308 return nfi.CurrencySymbol + " " + nfi.NegativeSign + s;
310 return s + nfi.NegativeSign + " " + nfi.CurrencySymbol;
312 return "(" + nfi.CurrencySymbol + " " + s + ")";
314 return "(" + s + " " + nfi.CurrencySymbol + ")";
316 throw new ArgumentException(Locale.GetText ("Invalid CurrencyNegativePattern"));
321 switch (nfi.CurrencyPositivePattern)
\r
324 return nfi.CurrencySymbol + s;
326 return s + nfi.CurrencySymbol;
328 return nfi.CurrencySymbol + " " + s;
330 return s + " " + nfi.CurrencySymbol;
332 throw new ArgumentException(Locale.GetText ("Invalid CurrencyPositivePattern"));
337 private static string FormatPercent(NumberFormatInfo nfi, StringBuilder sb,
338 int decimals, int decPos, int sign)
340 string s = FormatGroupAndDec(sb, decimals, decPos+2,
341 nfi.PercentGroupSizes, nfi.PercentGroupSeparator, nfi.PercentDecimalSeparator);
345 switch (nfi.PercentNegativePattern)
\r
348 return nfi.NegativeSign + s + " " + nfi.PercentSymbol;
350 return nfi.NegativeSign + s + nfi.PercentSymbol;
352 return nfi.NegativeSign + nfi.PercentSymbol + s;
354 throw new ArgumentException(Locale.GetText ("Invalid PercentNegativePattern"));
359 switch (nfi.PercentPositivePattern)
\r
362 return s + " " + nfi.PercentSymbol;
364 return s + nfi.PercentSymbol;
366 return nfi.PercentSymbol + s;
368 throw new ArgumentException("Invalid PercentPositivePattern");
374 private static string FormatNormalized(NumberFormatInfo nfi, StringBuilder sb,
375 int digits, int decPos, int sign)
377 //LAMESPEC: how should this format look like ? Is this a fixed point format ?
378 throw new NotImplementedException ();