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 format = format.Trim ();
61 if (!DecimalFormatter.ParseFormat(format, out specifier, out precision))
\r
63 throw new FormatException (Locale.GetText ("The specified format is invalid"));
\r
68 // first calculate number of digits or decimals needed for format
\r
72 decimals = (precision >= 0) ? precision : nfi.CurrencyDecimalDigits;
\r
74 case 'F': goto case 'N';
\r
76 decimals = (precision >= 0) ? precision : nfi.NumberDecimalDigits;
\r
79 digits = (precision >= 0) ? precision : 0;
\r
82 digits = (precision >= 0) ? precision+1 : 7;
\r
85 decimals = (precision >= 0) ? precision+2 : nfi.PercentDecimalDigits+2;
\r
93 const int bufSize = 40;
\r
94 int decPos = 0, sign = 0;
\r
95 char[] buf = new char[bufSize];
\r
96 if (Decimal.decimal2string(ref value, digits, decimals, buf, bufSize, out decPos, out sign) != 0)
\r
98 throw new FormatException(); // should never happen
\r
101 string TempString = new String(buf);
102 TempString = TempString.Trim(new char[] {(char)0x0});
103 StringBuilder sb = new StringBuilder(TempString, TempString.Length);
\r
105 if (sb.ToString () == String.Empty && decPos > 0 && sign == 0)
108 // now build the format
\r
111 case 'C': return FormatCurrency(nfi, sb, decimals, decPos, sign);
\r
112 case 'N': return FormatNumber(nfi, sb, decimals, decPos, sign);
\r
113 case 'F': return FormatFixedPoint(nfi, sb, decimals, decPos, sign);
\r
114 case 'G': return FormatGeneral(nfi, sb, digits, decPos, sign, format[0]);
\r
115 case 'E': return FormatExponential(nfi, sb, digits, decPos, sign, format[0], true);
\r
116 case 'P': return FormatPercent(nfi, sb, decimals, decPos, sign);
\r
117 case 'Z': return FormatNormalized(nfi, sb, digits, decPos, sign);
\r
119 throw new FormatException (Locale.GetText ("The specified format is invalid"));
\r
123 private static string FormatFixedPoint(NumberFormatInfo nfi, StringBuilder sb,
124 int decimals, int decPos, int sign)
128 sb.Insert((decPos <= 0) ? 1 : decPos, nfi.NumberDecimalSeparator);
133 sb.Insert(0, nfi.NegativeSign);
136 return sb.ToString();
139 private static string FormatExponential(NumberFormatInfo nfi, StringBuilder sb,
140 int digits, int decPos, int sign, char echar, bool exp3flag)
142 // insert decimal separator
143 if (digits > 1 || (digits == 0 && sb.Length > 1))
\r
145 sb.Insert(1, nfi.NumberDecimalSeparator);
151 sb.Insert(0, nfi.NegativeSign);
157 sb.Append((decPos >= 0) ? nfi.PositiveSign : nfi.NegativeSign);
158 if (decPos < 0) decPos *= -1;
159 if (exp3flag) sb.Append('0');
160 sb.Append((char)('0' + decPos/10));
161 sb.Append((char)('0' + decPos%10));
163 return sb.ToString();
166 private static string FormatGeneral(NumberFormatInfo nfi, StringBuilder sb,
167 int digits, int decPos, int sign, char gchar)
170 bool bFix = (digits == 0 && decPos >= -3) || (digits >= decPos && decPos >= -3 && digits != 0);
172 // remove trailing digits
173 while (sb.Length > 1 && (sb.Length > decPos || !bFix) && sb[sb.Length-1] == '0')
175 sb.Remove(sb.Length-1, 1);
181 while (decPos <= 0)
\r
184 if (dig != 0 && decPos != 0) dig++;
187 return FormatFixedPoint(nfi, sb, sb.Length - decPos, decPos, sign);
191 return FormatExponential(nfi, sb, dig, decPos, sign, (char)(gchar-2), false);
195 private static string FormatGroupAndDec(StringBuilder sb, int decimals, int decPos,
196 int[] groupSizes, string groupSeparator, string decSeparator)
203 if (groupSizes != null)
\r
207 for (int i = 0; i < groupSizes.GetLength(0); i++)
\r
209 int size = groupSizes[i];
213 if (digitCount < decPos)
\r
215 sb.Insert(decPos - digitCount, groupSeparator);
216 offset += groupSeparator.Length;
226 digitCount +=lastSize;
227 if (digitCount >= decPos) break;
228 sb.Insert(decPos - digitCount, groupSeparator);
229 offset += groupSeparator.Length;
235 if ((decimals > 0) && (decPos+offset < sb.Length))
237 sb.Insert(offset + ((decPos <= 0) ? 1 : decPos), decSeparator);
240 return sb.ToString();
243 private static string FormatNumber(NumberFormatInfo nfi, StringBuilder sb,
244 int decimals, int decPos, int sign)
246 string s = FormatGroupAndDec(sb, decimals, decPos,
247 nfi.NumberGroupSizes, nfi.NumberGroupSeparator, nfi.NumberDecimalSeparator);
252 switch (nfi.NumberNegativePattern)
\r
255 return "(" + s + ")";
257 return nfi.NegativeSign + s;
259 return nfi.NegativeSign + " " + s;
261 return s + nfi.NegativeSign;
263 return s + " " + nfi.NegativeSign;
265 throw new ArgumentException(Locale.GetText ("Invalid NumberNegativePattern"));
274 private static string FormatCurrency(NumberFormatInfo nfi, StringBuilder sb,
275 int decimals, int decPos, int sign)
277 string s = FormatGroupAndDec(sb, decimals, decPos,
278 nfi.CurrencyGroupSizes, nfi.CurrencyGroupSeparator, nfi.CurrencyDecimalSeparator);
282 switch (nfi.CurrencyNegativePattern)
\r
285 return "(" + nfi.CurrencySymbol + s + ")";
287 return nfi.NegativeSign + nfi.CurrencySymbol + s;
289 return nfi.CurrencySymbol + nfi.NegativeSign + s;
291 return nfi.CurrencySymbol + s + nfi.NegativeSign;
293 return "(" + s + nfi.CurrencySymbol + ")";
295 return nfi.NegativeSign + s + nfi.CurrencySymbol;
297 return s + nfi.NegativeSign + nfi.CurrencySymbol;
299 return s + nfi.CurrencySymbol + nfi.NegativeSign;
301 return nfi.NegativeSign + s + " " + nfi.CurrencySymbol;
303 return nfi.NegativeSign + nfi.CurrencySymbol + " " + s;
305 return s + " " + nfi.CurrencySymbol + nfi.NegativeSign;
307 return nfi.CurrencySymbol + " " + s + nfi.NegativeSign;
309 return nfi.CurrencySymbol + " " + nfi.NegativeSign + s;
311 return s + nfi.NegativeSign + " " + nfi.CurrencySymbol;
313 return "(" + nfi.CurrencySymbol + " " + s + ")";
315 return "(" + s + " " + nfi.CurrencySymbol + ")";
317 throw new ArgumentException(Locale.GetText ("Invalid CurrencyNegativePattern"));
322 switch (nfi.CurrencyPositivePattern)
\r
325 return nfi.CurrencySymbol + s;
327 return s + nfi.CurrencySymbol;
329 return nfi.CurrencySymbol + " " + s;
331 return s + " " + nfi.CurrencySymbol;
333 throw new ArgumentException(Locale.GetText ("Invalid CurrencyPositivePattern"));
338 private static string FormatPercent(NumberFormatInfo nfi, StringBuilder sb,
339 int decimals, int decPos, int sign)
341 string s = FormatGroupAndDec(sb, decimals, decPos+2,
342 nfi.PercentGroupSizes, nfi.PercentGroupSeparator, nfi.PercentDecimalSeparator);
346 switch (nfi.PercentNegativePattern)
\r
349 return nfi.NegativeSign + s + " " + nfi.PercentSymbol;
351 return nfi.NegativeSign + s + nfi.PercentSymbol;
353 return nfi.NegativeSign + nfi.PercentSymbol + s;
355 throw new ArgumentException(Locale.GetText ("Invalid PercentNegativePattern"));
360 switch (nfi.PercentPositivePattern)
\r
363 return s + " " + nfi.PercentSymbol;
365 return s + nfi.PercentSymbol;
367 return nfi.PercentSymbol + s;
369 throw new ArgumentException("Invalid PercentPositivePattern");
375 private static string FormatNormalized(NumberFormatInfo nfi, StringBuilder sb,
376 int digits, int decPos, int sign)
378 //LAMESPEC: how should this format look like ? Is this a fixed point format ?
379 throw new NotImplementedException ();