2 // System.IntegerFormatter.cs
5 // Derek Holden (dholden@draper.com)
7 // (C) Derek Holden dholden@draper.com
11 // Format integer types. Completely based off ECMA docs
12 // for IFormattable specification. Has been tested w/
13 // all integral types, from boundry to boundry, w/ all
14 // formats A## ("G", "G0" ... "G99", "P", "P0" ... "P99").
16 // If you make any changes, please make sure to check the
17 // boundry format precisions (0, 99) and the min / max values
18 // of the data types (Int32.[Max/Min]Value).
20 // Using int as an example, it is currently set up as
24 // public string ToString (string format, NumberFormatInfo nfi) {
25 // return IntegerFormatter.NumberToString (format, nfi, value);
29 // public string NumberToString (string format, NumberFormatInfo nfi, int value) {
30 // ParseFormat (format);
31 // switch (format type) {
32 // case 'G' FormatGeneral(value, precision);
33 // case 'R' throw Exception("Invalid blah blah");
34 // case 'C' FromatCurrency(value, precision, nfi);
40 // There is a property in NumberFormatInfo for NegativeSign, though the
41 // definition of IFormattable just uses '-' in context. So all the
42 // hardcoded uses of '-' in here may need to be changed to nfi.NegativeSign
44 // For every integral type.
46 // Before every Format<Format Type> block there is a small paragraph
47 // detailing its requirements, and a blurb of what I was thinking
50 // Some speedup suggestions to be done when after this appears
51 // to be working properly:
53 // * Deal w/ out of range numbers better. Specifically with
54 // regards to boundry cases such as Long.MinValue etc.
55 // The previous way of if (value < 0) value = -value;
56 // fails under this assumption, since the largest
57 // possible MaxValue is < absolute value of the MinValue.
58 // I do the first iteration outside of the loop, and then
59 // convert the number to positive, then continue in the loop.
61 // * Replace all occurances of max<Type>Length with their
62 // numerical values. Plus the places where things are set
63 // to max<Type>Length - 1. Hardcode these to numbers.
65 // * Move the code for all the NumberToString()'s into the
66 // the main ToString (string, NumberFormatInfo) method in
67 // the data types themselves. That way they'd be throwing
68 // their own exceptions on error and it'd save a function
71 // * For integer to char buffer transformation, you could
72 // implement the calculations of the 10's and 100's place
73 // the same time w/ another table to shorten loop time.
75 // * Someone smarter can prolly find a much more efficient
76 // way of formatting the exponential notation. It's still
77 // done in pass, just may have too many repositioning
80 // * Decide whether it be better to have functions that
81 // handle formatting for all types, or just cast their
82 // values out and format them. Just if library size is
83 // more important than speed in saving a cast and a
88 using System.Collections;
89 using System.Globalization;
93 public sealed class IntegerFormatter {
95 private static int maxByteLength = 4;
96 private static int maxShortLength = 6;
97 private static int maxIntLength = 12;
98 private static int maxLongLength = 22;
100 private static char[] digitLowerTable;
102 * This makes a TypeNotInitialized exception be thrown.
103 * { '0', '1', '2', '3', '4', '5', '6', '7',
104 * '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
107 private static char[] digitUpperTable;
109 * { '0', '1', '2', '3', '4', '5', '6', '7',
110 * '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
113 static IntegerFormatter ()
117 digitLowerTable = new char[16];
118 digitUpperTable = new char[16];
120 for (i = 0; i < 10; i++){
121 digitLowerTable[i] = (char) ('0' + i);
122 digitUpperTable[i] = (char) ('0' + i);
125 char lc = (char ) ('a' - i);
126 char uc = (char ) ('A' - i);
128 digitLowerTable[i] = (char) (lc + i);
129 digitUpperTable[i] = (char) (uc + i);
134 private static bool IsDigit (char c)
136 return !(c < '0' || c > '9');
139 private static bool IsLetter (char c)
141 return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
144 private static bool ParseFormat (string format, out char specifier, out int precision, out bool custom)
150 int length = format.Length;
151 // TODO: Could an empty string be a custom format string?
155 char[] chars = format.ToCharArray ();
156 specifier = chars[0];
158 // TODO: IsLetter() and IsDigit() should be replaced by Char.Is*()
159 if (IsLetter(specifier) && length <= 3) {
164 if (IsDigit(chars[1])) {
165 precision = chars[1] - '0';
170 if (IsDigit(chars[1]) && IsDigit(chars[2])) {
171 precision = chars[1] - '0';
172 precision = precision * 10 + chars[2] - '0';
180 // We've got a custom format string.
185 // ============ Public Interface to all the integer types ============ //
187 public static string NumberToString (string format, NumberFormatInfo nfi, byte value)
193 if (!ParseFormat (format, out specifier, out precision, out custom))
194 throw new FormatException (Locale.GetText ("The specified format '" + format + "' is invalid"));
197 return FormatCustom (format, value, nfi);
201 case 'c': return FormatCurrency (value, precision, nfi);
202 case 'C': return FormatCurrency (value, precision, nfi);
203 case 'd': return FormatDecimal (value, precision);
204 case 'D': return FormatDecimal (value, precision);
205 case 'e': return FormatExponential (value, precision, false);
206 case 'E': return FormatExponential (value, precision, true);
207 case 'f': return FormatFixedPoint (value, precision, nfi);
208 case 'F': return FormatFixedPoint (value, precision, nfi);
209 case 'g': return FormatGeneral (value, precision, nfi, false);
210 case 'G': return FormatGeneral (value, precision, nfi, true);
211 case 'n': return FormatNumber (value, precision, nfi);
212 case 'N': return FormatNumber (value, precision, nfi);
213 case 'p': return FormatPercent (value, precision, nfi);
214 case 'P': return FormatPercent (value, precision, nfi);
215 case 'r': throw new FormatException (Locale.GetText ("The specified format cannot be used in this instance"));
216 case 'R': throw new FormatException (Locale.GetText ("The specified format cannot be used in this instance"));
217 case 'x': return FormatHexadecimal (value, precision, false);
218 case 'X': return FormatHexadecimal (value, precision, true);
220 throw new FormatException (Locale.GetText ("The specified format '" + format + "' is invalid"));
224 public static string NumberToString (string format, NumberFormatInfo nfi, short value)
230 if (!ParseFormat (format, out specifier, out precision, out custom))
231 throw new FormatException (Locale.GetText ("The specified format '" + format + "' is invalid"));
234 return FormatCustom (format, value, nfi);
238 case 'c': return FormatCurrency (value, precision, nfi);
239 case 'C': return FormatCurrency (value, precision, nfi);
240 case 'd': return FormatDecimal (value, precision);
241 case 'D': return FormatDecimal (value, precision);
242 case 'e': return FormatExponential (value, precision, false);
243 case 'E': return FormatExponential (value, precision, true);
244 case 'f': return FormatFixedPoint (value, precision, nfi);
245 case 'F': return FormatFixedPoint (value, precision, nfi);
246 case 'g': return FormatGeneral (value, precision, nfi, false);
247 case 'G': return FormatGeneral (value, precision, nfi, true);
248 case 'n': return FormatNumber (value, precision, nfi);
249 case 'N': return FormatNumber (value, precision, nfi);
250 case 'p': return FormatPercent (value, precision, nfi);
251 case 'P': return FormatPercent (value, precision, nfi);
252 case 'r': throw new FormatException (Locale.GetText ("The specified format cannot be used in this instance"));
253 case 'R': throw new FormatException (Locale.GetText ("The specified format cannot be used in this insance"));
254 case 'x': return FormatHexadecimal (value, precision, false);
255 case 'X': return FormatHexadecimal (value, precision, true);
257 throw new FormatException (Locale.GetText ("The specified format '" + format + "' is invalid"));
261 public static string NumberToString (string format, NumberFormatInfo nfi, int value)
267 if (!ParseFormat (format, out specifier, out precision, out custom))
268 throw new FormatException (Locale.GetText ("The specified format '" + format + "' is invalid"));
271 return FormatCustom (format, value, nfi);
275 case 'c': return FormatCurrency (value, precision, nfi);
276 case 'C': return FormatCurrency (value, precision, nfi);
277 case 'd': return FormatDecimal (value, precision);
278 case 'D': return FormatDecimal (value, precision);
279 case 'e': return FormatExponential (value, precision, false);
280 case 'E': return FormatExponential (value, precision, true);
281 case 'f': return FormatFixedPoint (value, precision, nfi);
282 case 'F': return FormatFixedPoint (value, precision, nfi);
283 case 'g': return FormatGeneral (value, precision, nfi, false);
284 case 'G': return FormatGeneral (value, precision, nfi, true);
285 case 'n': return FormatNumber (value, precision, nfi);
286 case 'N': return FormatNumber (value, precision, nfi);
287 case 'p': return FormatPercent (value, precision, nfi);
288 case 'P': return FormatPercent (value, precision, nfi);
289 case 'r': throw new FormatException (Locale.GetText ("The specified format cannot be used in this instance"));
290 case 'R': throw new FormatException (Locale.GetText ("The specified format cannot be used in this instance"));
291 case 'x': return FormatHexadecimal (value, precision, false);
292 case 'X': return FormatHexadecimal (value, precision, true);
294 throw new FormatException (Locale.GetText ("The specified format '" + format + "' is invalid"));
298 public static string NumberToString (string format, NumberFormatInfo nfi, long value)
304 if (!ParseFormat (format, out specifier, out precision, out custom))
305 throw new FormatException (Locale.GetText ("The specified format '" + format + "' is invalid"));
308 return FormatCustom (format, value, nfi);
312 case 'c': return FormatCurrency (value, precision, nfi);
313 case 'C': return FormatCurrency (value, precision, nfi);
314 case 'd': return FormatDecimal (value, precision);
315 case 'D': return FormatDecimal (value, precision);
316 case 'e': return FormatExponential (value, precision, false);
317 case 'E': return FormatExponential (value, precision, true);
318 case 'f': return FormatFixedPoint (value, precision, nfi);
319 case 'F': return FormatFixedPoint (value, precision, nfi);
320 case 'g': return FormatGeneral (value, precision, nfi, false);
321 case 'G': return FormatGeneral (value, precision, nfi, true);
322 case 'n': return FormatNumber (value, precision, nfi);
323 case 'N': return FormatNumber (value, precision, nfi);
324 case 'p': return FormatPercent (value, precision, nfi);
325 case 'P': return FormatPercent (value, precision, nfi);
326 case 'r': throw new FormatException (Locale.GetText ("The specified format cannot be used in this instance"));
327 case 'R': throw new FormatException (Locale.GetText ("The specified format cannot be used in this instance"));
328 case 'x': return FormatHexadecimal (value, precision, false);
329 case 'X': return FormatHexadecimal (value, precision, true);
331 throw new FormatException (Locale.GetText ("The specified format '" + format + "' is invalid"));
335 [CLSCompliant (false)]
336 public static string NumberToString (string format, NumberFormatInfo nfi, sbyte value)
342 if (!ParseFormat (format, out specifier, out precision, out custom))
343 throw new FormatException (Locale.GetText ("The specified format '" + format + "' is invalid"));
346 return FormatCustom (format, value, nfi);
350 case 'c': return FormatCurrency (value, precision, nfi);
351 case 'C': return FormatCurrency (value, precision, nfi);
352 case 'd': return FormatDecimal (value, precision);
353 case 'D': return FormatDecimal (value, precision);
354 case 'e': return FormatExponential (value, precision, false);
355 case 'E': return FormatExponential (value, precision, true);
356 case 'f': return FormatFixedPoint (value, precision, nfi);
357 case 'F': return FormatFixedPoint (value, precision, nfi);
358 case 'g': return FormatGeneral (value, precision, nfi, false);
359 case 'G': return FormatGeneral (value, precision, nfi, true);
360 case 'n': return FormatNumber (value, precision, nfi);
361 case 'N': return FormatNumber (value, precision, nfi);
362 case 'p': return FormatPercent (value, precision, nfi);
363 case 'P': return FormatPercent (value, precision, nfi);
364 case 'r': throw new FormatException (Locale.GetText ("The specified format cannot be used in this instance"));
365 case 'R': throw new FormatException (Locale.GetText ("The specified format cannot be used in this instance"));
366 case 'x': return FormatHexadecimal (value, precision, false);
367 case 'X': return FormatHexadecimal (value, precision, true);
369 throw new FormatException (Locale.GetText ("The specified format '" + format + "' is invalid"));
373 [CLSCompliant (false)]
374 public static string NumberToString (string format, NumberFormatInfo nfi, ushort value)
380 if (!ParseFormat (format, out specifier, out precision, out custom))
381 throw new FormatException (Locale.GetText ("The specified format '" + format + "' is invalid"));
384 return FormatCustom (format, value, nfi);
388 case 'c': return FormatCurrency (value, precision, nfi);
389 case 'C': return FormatCurrency (value, precision, nfi);
390 case 'd': return FormatDecimal (value, precision);
391 case 'D': return FormatDecimal (value, precision);
392 case 'e': return FormatExponential (value, precision, false);
393 case 'E': return FormatExponential (value, precision, true);
394 case 'f': return FormatFixedPoint (value, precision, nfi);
395 case 'F': return FormatFixedPoint (value, precision, nfi);
396 case 'g': return FormatGeneral (value, precision, nfi, false);
397 case 'G': return FormatGeneral (value, precision, nfi, true);
398 case 'n': return FormatNumber (value, precision, nfi);
399 case 'N': return FormatNumber (value, precision, nfi);
400 case 'p': return FormatPercent (value, precision, nfi);
401 case 'P': return FormatPercent (value, precision, nfi);
402 case 'r': throw new FormatException (Locale.GetText ("The specified format cannot be used in this instance"));
403 case 'R': throw new FormatException (Locale.GetText ("The specified format cannot be used in this instance"));
404 case 'x': return FormatHexadecimal (value, precision, false);
405 case 'X': return FormatHexadecimal (value, precision, true);
407 throw new FormatException (Locale.GetText ("The specified format '" + format + "' is invalid"));
411 [CLSCompliant (false)]
412 public static string NumberToString (string format, NumberFormatInfo nfi, uint value)
418 if (!ParseFormat (format, out specifier, out precision, out custom))
419 throw new FormatException (Locale.GetText ("The specified format '" + format + "' is invalid"));
422 return FormatCustom (format, value, nfi);
426 case 'c': return FormatCurrency (value, precision, nfi);
427 case 'C': return FormatCurrency (value, precision, nfi);
428 case 'd': return FormatDecimal (value, precision);
429 case 'D': return FormatDecimal (value, precision);
430 case 'e': return FormatExponential (value, precision, false);
431 case 'E': return FormatExponential (value, precision, true);
432 case 'f': return FormatFixedPoint (value, precision, nfi);
433 case 'F': return FormatFixedPoint (value, precision, nfi);
434 case 'g': return FormatGeneral (value, precision, nfi, false);
435 case 'G': return FormatGeneral (value, precision, nfi, true);
436 case 'n': return FormatNumber (value, precision, nfi);
437 case 'N': return FormatNumber (value, precision, nfi);
438 case 'p': return FormatPercent (value, precision, nfi);
439 case 'P': return FormatPercent (value, precision, nfi);
440 case 'r': throw new FormatException (Locale.GetText ("The specified format cannot be used in this instance"));
441 case 'R': throw new FormatException (Locale.GetText ("The specified format cannot be used in this instance"));
442 case 'x': return FormatHexadecimal (value, precision, false);
443 case 'X': return FormatHexadecimal (value, precision, true);
445 throw new FormatException (Locale.GetText ("The specified format '" + format + "' is invalid"));
449 [CLSCompliant (false)]
450 public static string NumberToString (string format, NumberFormatInfo nfi, ulong value)
456 if (!ParseFormat (format, out specifier, out precision, out custom))
457 throw new FormatException (Locale.GetText ("The specified format '" + format + "' is invalid"));
460 return FormatCustom (format, value, nfi);
464 case 'c': return FormatCurrency (value, precision, nfi);
465 case 'C': return FormatCurrency (value, precision, nfi);
466 case 'd': return FormatDecimal (value, precision);
467 case 'D': return FormatDecimal (value, precision);
468 case 'e': return FormatExponential (value, precision, false);
469 case 'E': return FormatExponential (value, precision, true);
470 case 'f': return FormatFixedPoint (value, precision, nfi);
471 case 'F': return FormatFixedPoint (value, precision, nfi);
472 case 'g': return FormatGeneral (value, precision, nfi, false);
473 case 'G': return FormatGeneral (value, precision, nfi, true);
474 case 'n': return FormatNumber (value, precision, nfi);
475 case 'N': return FormatNumber (value, precision, nfi);
476 case 'p': return FormatPercent (value, precision, nfi);
477 case 'P': return FormatPercent (value, precision, nfi);
478 case 'r': throw new FormatException (Locale.GetText ("The specified format cannot be used in this instance"));
479 case 'R': throw new FormatException (Locale.GetText ("The specified format cannot be used in this instance"));
480 case 'x': return FormatHexadecimal (value, precision, false);
481 case 'X': return FormatHexadecimal (value, precision, true);
483 throw new FormatException (Locale.GetText ("The specified format '" + format + "' is invalid"));
487 // ============ Currency Type Formating ============ //
490 // Currency Format: Used for strings containing a monetary value. The
491 // CurrencySymbol, CurrencyGroupSizes, CurrencyGroupSeparator, and
492 // CurrencyDecimalSeparator members of a NumberFormatInfo supply
493 // the currency symbol, size and separator for digit groupings, and
494 // decimal separator, respectively.
495 // CurrencyNegativePattern and CurrencyPositivePattern determine the
496 // symbols used to represent negative and positive values. For example,
497 // a negative value may be prefixed with a minus sign, or enclosed in
499 // If the precision specifier is omitted
500 // NumberFormatInfo.CurrencyDecimalDigits determines the number of
501 // decimal places in the string. Results are rounded to the nearest
502 // representable value when necessary.
504 // The pattern of the NumberFormatInfo determines how the output looks, where
505 // the dollar sign goes, where the negative sign goes, etc.
506 // IFormattable documentation lists the patterns and their values,
507 // I have them commented out in the large switch statement
510 private static string FormatCurrency (byte value, int precision, NumberFormatInfo nfi)
512 return FormatCurrency ((uint)value, precision, nfi);
515 private static string FormatCurrency (short value, int precision, NumberFormatInfo nfi)
517 return FormatCurrency ((int)value, precision, nfi);
520 private static string FormatCurrency (int value, int precision, NumberFormatInfo nfi)
523 bool negative = (value < 0);
525 char[] groupSeparator = nfi.CurrencyGroupSeparator.ToCharArray ();
526 char[] decimalSeparator = nfi.CurrencyDecimalSeparator.ToCharArray ();
527 char[] currencySymbol = nfi.CurrencySymbol.ToCharArray ();
528 int[] groupSizes = nfi.CurrencyGroupSizes;
529 int pattern = negative ? nfi.CurrencyNegativePattern : nfi.CurrencyPositivePattern;
530 int symbolLength = currencySymbol.Length;
532 int padding = (precision >= 0) ? precision : nfi.CurrencyDecimalDigits;
533 int size = maxIntLength + (groupSeparator.Length * maxIntLength) + padding + 2 +
534 decimalSeparator.Length + symbolLength;
535 char[] buffy = new char[size];
538 // set up the pattern from IFormattible
544 buffy[--position] = ')';
551 buffy[--position] = '-';
554 buffy[--position] = ')';
556 buffy[--position] = currencySymbol[--i];
561 buffy[--position] = currencySymbol[--i];
566 buffy[--position] = currencySymbol[--i];
568 buffy[--position] = '-';
571 buffy[--position] = '-';
573 buffy[--position] = currencySymbol[--i];
578 buffy[--position] = currencySymbol[--i];
580 buffy[--position] = ' ';
585 buffy[--position] = '-';
587 buffy[--position] = currencySymbol[--i];
589 buffy[--position] = ' ';
592 buffy[--position] = '-';
594 // case 12: // $ -nnn
598 buffy[--position] = currencySymbol[--i];
600 buffy[--position] = ' ';
601 buffy[--position] = '-';
604 buffy[--position] = ')';
607 buffy[--position] = ')';
609 buffy[--position] = currencySymbol[--i];
620 buffy[--position] = currencySymbol[--i];
627 buffy[--position] = currencySymbol[--i];
629 buffy[--position] = ' ';
634 // right pad it w/ precision 0's
635 while (padding-- > 0)
636 buffy[--position] = '0';
638 // put on decimal separator if we moved over and put a 0
639 if (position < size && buffy[position] == '0') {
640 i = decimalSeparator.Length;
642 buffy[--position] = decimalSeparator[--i];
646 // loop through, keeping track of where you are in the
647 // group sizes array and putting out the group separator
652 // just in place to take care of the negative boundries (Int32.MinValue)
655 buffy[--position] = digitLowerTable[-(value % 10)];
659 i = groupSeparator.Length;
661 buffy[--position] = groupSeparator[--i];
664 k = (j < groupSizes.Length) ?
665 groupSizes[j++] : groupSizes[(groupSizes.Length - 1)];
667 } else value = -value;
670 while (value >= 10) {
671 buffy[--position] = digitLowerTable[(value % 10)];
675 i = groupSeparator.Length;
677 buffy[--position] = groupSeparator[--i];
680 k = (j < groupSizes.Length) ?
681 groupSizes[j++] : groupSizes[(groupSizes.Length - 1)];
685 buffy[--position] = digitLowerTable[value];
687 // end the pattern on the left hand side
694 buffy[--position] = currencySymbol[--i];
696 buffy[--position] = '(';
700 buffy[--position] = currencySymbol[--i];
702 buffy[--position] = '-';
705 buffy[--position] = '-';
707 buffy[--position] = currencySymbol[--i];
712 buffy[--position] = currencySymbol[--i];
716 buffy[--position] = '(';
719 buffy[--position] = '-';
726 buffy[--position] = '-';
730 // case 10: // nnn $-
733 buffy[--position] = ' ';
735 buffy[--position] = currencySymbol[--i];
739 buffy[--position] = '-';
740 buffy[--position] = ' ';
742 buffy[--position] = currencySymbol[--i];
745 // case 13: // nnn- $
748 buffy[--position] = ' ';
750 buffy[--position] = currencySymbol[--i];
752 buffy[--position] = '(';
755 buffy[--position] = '(';
763 buffy[--position] = currencySymbol[--i];
769 buffy[--position] = ' ';
771 buffy[--position] = currencySymbol[--i];
779 return new string (buffy, position, (size - position));
782 private static string FormatCurrency (long value, int precision, NumberFormatInfo nfi)
785 bool negative = (value < 0);
787 char[] groupSeparator = nfi.CurrencyGroupSeparator.ToCharArray ();
788 char[] decimalSeparator = nfi.CurrencyDecimalSeparator.ToCharArray ();
789 char[] currencySymbol = nfi.CurrencySymbol.ToCharArray ();
790 int[] groupSizes = nfi.CurrencyGroupSizes;
791 int pattern = negative ? nfi.CurrencyNegativePattern : nfi.CurrencyPositivePattern;
792 int symbolLength = currencySymbol.Length;
794 int padding = (precision >= 0) ? precision : nfi.CurrencyDecimalDigits;
795 int size = maxLongLength + (groupSeparator.Length * maxLongLength) + padding + 2 +
796 decimalSeparator.Length + symbolLength;
797 char[] buffy = new char[size];
800 // set up the pattern from IFormattible
806 buffy[--position] = ')';
813 buffy[--position] = '-';
816 buffy[--position] = ')';
818 buffy[--position] = currencySymbol[--i];
823 buffy[--position] = currencySymbol[--i];
828 buffy[--position] = currencySymbol[--i];
830 buffy[--position] = '-';
833 buffy[--position] = '-';
835 buffy[--position] = currencySymbol[--i];
840 buffy[--position] = currencySymbol[--i];
842 buffy[--position] = ' ';
847 buffy[--position] = '-';
849 buffy[--position] = currencySymbol[--i];
851 buffy[--position] = ' ';
854 buffy[--position] = '-';
856 // case 12: // $ -nnn
860 buffy[--position] = currencySymbol[--i];
862 buffy[--position] = ' ';
863 buffy[--position] = '-';
866 buffy[--position] = ')';
869 buffy[--position] = ')';
871 buffy[--position] = currencySymbol[--i];
882 buffy[--position] = currencySymbol[--i];
889 buffy[--position] = currencySymbol[--i];
891 buffy[--position] = ' ';
896 // right pad it w/ precision 0's
897 while (padding-- > 0)
898 buffy[--position] = '0';
900 // put on decimal separator if we moved over and put a 0
901 if (position < size && buffy[position] == '0') {
902 i = decimalSeparator.Length;
904 buffy[--position] = decimalSeparator[--i];
908 // loop through, keeping track of where you are in the
909 // group sizes array and putting out the group separator
916 buffy[--position] = digitLowerTable[-(value % 10)];
920 i = groupSeparator.Length;
922 buffy[--position] = groupSeparator[--i];
925 k = (j < groupSizes.Length) ?
926 groupSizes[j++] : groupSizes[(groupSizes.Length - 1)];
928 } else value = -value;
931 while (value >= 10) {
932 buffy[--position] = digitLowerTable[(value % 10)];
936 i = groupSeparator.Length;
938 buffy[--position] = groupSeparator[--i];
941 k = (j < groupSizes.Length) ?
942 groupSizes[j++] : groupSizes[(groupSizes.Length - 1)];
946 buffy[--position] = digitLowerTable[value];
948 // end the pattern on the left hand side
955 buffy[--position] = currencySymbol[--i];
957 buffy[--position] = '(';
961 buffy[--position] = currencySymbol[--i];
963 buffy[--position] = '-';
966 buffy[--position] = '-';
968 buffy[--position] = currencySymbol[--i];
973 buffy[--position] = currencySymbol[--i];
977 buffy[--position] = '(';
980 buffy[--position] = '-';
987 buffy[--position] = '-';
991 // case 10: // nnn $-
994 buffy[--position] = ' ';
996 buffy[--position] = currencySymbol[--i];
1000 buffy[--position] = '-';
1001 buffy[--position] = ' ';
1003 buffy[--position] = currencySymbol[--i];
1006 // case 13: // nnn- $
1009 buffy[--position] = ' ';
1011 buffy[--position] = currencySymbol[--i];
1013 buffy[--position] = '(';
1016 buffy[--position] = '(';
1024 buffy[--position] = currencySymbol[--i];
1030 buffy[--position] = ' ';
1032 buffy[--position] = currencySymbol[--i];
1040 return new string (buffy, position, (size - position));
1043 private static string FormatCurrency (sbyte value, int precision, NumberFormatInfo nfi)
1045 return FormatCurrency ((int)value, precision, nfi);
1048 private static string FormatCurrency (ushort value, int precision, NumberFormatInfo nfi)
1050 return FormatCurrency ((uint)value, precision, nfi);
1053 private static string FormatCurrency (uint value, int precision, NumberFormatInfo nfi)
1057 char[] groupSeparator = nfi.CurrencyGroupSeparator.ToCharArray ();
1058 char[] decimalSeparator = nfi.CurrencyDecimalSeparator.ToCharArray ();
1059 char[] currencySymbol = nfi.CurrencySymbol.ToCharArray ();
1060 int[] groupSizes = nfi.CurrencyGroupSizes;
1061 int pattern = nfi.CurrencyPositivePattern;
1062 int symbolLength = currencySymbol.Length;
1064 int padding = (precision >= 0) ? precision : nfi.CurrencyDecimalDigits;
1065 int size = maxIntLength + (groupSeparator.Length * maxIntLength) + padding + 2 +
1066 decimalSeparator.Length + symbolLength;
1067 char[] buffy = new char[size];
1068 int position = size;
1070 // set up the pattern from IFormattible, no negative
1077 buffy[--position] = currencySymbol[--i];
1084 buffy[--position] = currencySymbol[--i];
1086 buffy[--position] = ' ';
1090 // right pad it w/ precision 0's
1091 while (padding-- > 0)
1092 buffy[--position] = '0';
1094 // put on decimal separator if we moved over and put a 0
1095 if (position < size && buffy[position] == '0') {
1096 i = decimalSeparator.Length;
1098 buffy[--position] = decimalSeparator[--i];
1102 // loop through, keeping track of where you are in the
1103 // group sizes array and putting out the group separator
1106 k = groupSizes[j++];
1108 while (value >= 10) {
1109 buffy[--position] = digitLowerTable[(value % 10)];
1113 i = groupSeparator.Length;
1115 buffy[--position] = groupSeparator[--i];
1118 k = (j < groupSizes.Length) ?
1119 groupSizes[j++] : groupSizes[(groupSizes.Length - 1)];
1123 buffy[--position] = digitLowerTable[value];
1125 // end the pattern on the left hand side
1130 buffy[--position] = currencySymbol[--i];
1136 buffy[--position] = ' ';
1138 buffy[--position] = currencySymbol[--i];
1145 return new string (buffy, position, (size - position));
1148 private static string FormatCurrency (ulong value, int precision, NumberFormatInfo nfi)
1152 char[] groupSeparator = nfi.CurrencyGroupSeparator.ToCharArray ();
1153 char[] decimalSeparator = nfi.CurrencyDecimalSeparator.ToCharArray ();
1154 char[] currencySymbol = nfi.CurrencySymbol.ToCharArray ();
1155 int[] groupSizes = nfi.CurrencyGroupSizes;
1156 int pattern = nfi.CurrencyPositivePattern;
1157 int symbolLength = currencySymbol.Length;
1159 int padding = (precision >= 0) ? precision : nfi.CurrencyDecimalDigits;
1160 int size = maxLongLength + (groupSeparator.Length * maxLongLength) + padding + 2 +
1161 decimalSeparator.Length + symbolLength;
1162 char[] buffy = new char[size];
1163 int position = size;
1165 // set up the pattern from IFormattible, no negative
1172 buffy[--position] = currencySymbol[--i];
1179 buffy[--position] = currencySymbol[--i];
1181 buffy[--position] = ' ';
1185 // right pad it w/ precision 0's
1186 while (padding-- > 0)
1187 buffy[--position] = '0';
1189 // put on decimal separator if we moved over and put a 0
1190 if (position < size && buffy[position] == '0') {
1191 i = decimalSeparator.Length;
1193 buffy[--position] = decimalSeparator[--i];
1197 // loop through, keeping track of where you are in the
1198 // group sizes array and putting out the group separator
1201 k = groupSizes[j++];
1203 while (value >= 10) {
1204 buffy[--position] = digitLowerTable[(value % 10)];
1208 i = groupSeparator.Length;
1210 buffy[--position] = groupSeparator[--i];
1213 k = (j < groupSizes.Length) ?
1214 groupSizes[j++] : groupSizes[(groupSizes.Length - 1)];
1218 buffy[--position] = digitLowerTable[value];
1220 // end the pattern on the left hand side
1225 buffy[--position] = currencySymbol[--i];
1231 buffy[--position] = ' ';
1233 buffy[--position] = currencySymbol[--i];
1240 return new string (buffy, position, (size - position));
1243 // ============ Format Decimal Types ============ //
1246 // Used only for integral data types. Negative values are
1247 // represented by using a '-' sign. The precision specifies
1248 // how many digits are to appear in the string. If it is >
1249 // how many digits we need, the left side is padded w/ 0's.
1250 // If it is smaller than what we need, it is discarded.
1252 // Fairly simple implementation. Fill the buffer from right
1253 // to left w/ numbers, then if we still have precision left
1254 // over, pad w/ zeros.
1257 private static string FormatDecimal (byte value, int precision)
1259 return FormatDecimal ((uint)value, precision);
1262 private static string FormatDecimal (short value, int precision)
1264 return FormatDecimal ((int)value, precision);
1267 private static string FormatDecimal (int value, int precision)
1269 int size = (precision > 0) ? (maxIntLength + precision) : maxIntLength;
1270 char[] buffy = new char[size];
1271 int position = size;
1272 bool negative = (value < 0);
1276 buffy[--position] = digitLowerTable[-(value % 10)];
1277 value = value / -10;
1278 } else value = -value;
1280 // get our value into a buffer from right to left
1281 while (value >= 10) {
1282 buffy[--position] = digitLowerTable[(value % 10)];
1286 buffy[--position] = digitLowerTable[value];
1288 // if we have precision left over, fill with 0's
1289 precision -= (size - position);
1290 while (precision-- > 0 && position > 1)
1291 buffy[--position] = '0';
1294 buffy[--position] = '-';
1296 return new string (buffy, position, (size - position));
1299 private static string FormatDecimal (long value, int precision)
1301 int size = (precision > 0) ? (maxLongLength + precision) : maxLongLength;
1302 char[] buffy = new char[size];
1303 int position = size;
1304 bool negative = (value < 0);
1308 buffy[--position] = digitLowerTable[-(value % 10)];
1309 value = value / -10;
1310 } else value = -value;
1312 // get our value into a buffer from right to left
1313 while (value >= 10) {
1314 buffy[--position] = digitLowerTable[(value % 10)];
1318 buffy[--position] = digitLowerTable[value];
1320 // if we have precision left over, fill with 0's
1321 precision -= (size - position);
1322 while (precision-- > 0 && position > 1)
1323 buffy[--position] = '0';
1326 buffy[--position] = '-';
1328 return new string (buffy, position, (size - position));
1331 private static string FormatDecimal (sbyte value, int precision)
1333 return FormatDecimal ((int)value, precision);
1336 private static string FormatDecimal (ushort value, int precision)
1338 return FormatDecimal ((uint)value, precision);
1341 private static string FormatDecimal (uint value, int precision)
1343 int size = (precision > 0) ? (maxIntLength + precision) : maxIntLength;
1344 char[] buffy = new char[size];
1345 int position = size;
1347 // get our value into a buffer from right to left
1348 while (value >= 10) {
1349 buffy[--position] = digitLowerTable[(value % 10)];
1353 buffy[--position] = digitLowerTable[value];
1355 // if we have precision left over, fill with 0's
1356 precision -= (size - position);
1357 while (precision-- > 0 && position > 1)
1358 buffy[--position] = '0';
1360 return new string (buffy, position, (size - position));
1363 private static string FormatDecimal (ulong value, int precision)
1365 int size = (precision > 0) ? (maxLongLength + precision) : maxLongLength;
1366 char[] buffy = new char[size];
1367 int position = size;
1369 // get our value into a buffer from right to left
1370 while (value >= 10) {
1371 buffy[--position] = digitLowerTable[(value % 10)];
1375 buffy[--position] = digitLowerTable[value];
1377 // if we have precision left over, fill with 0's
1378 precision -= (size - position);
1379 while (precision-- > 0 && position > 1)
1380 buffy[--position] = '0';
1382 return new string (buffy, position, (size - position));
1385 // ============ Format Exponentials ============ //
1388 // Used for strings in the format [-]M.DDDDDDe+XXX.
1389 // Exaclty one non-zero digit must appear in M, w/
1390 // a '-' sign if negative. The precision determines
1391 // number of decimal places, if not given go 6 places.
1392 // If precision > the number of places we need, it
1393 // is right padded w/ 0's. If it is smaller than what
1394 // we need, we cut off and round. The format specifier
1395 // decides whether we use an uppercase E or lowercase e.
1397 // Tried to do this in one pass of one buffer, but it
1398 // wasn't happening. Get a buffer + 7 extra slots for
1399 // the -, ., E, +, and XXX. Parse the value into another
1400 // temp buffer, then build the new string. For the
1401 // integral data types, there are a couple things that
1402 // can be hardcoded. Since an int and a long can't be
1403 // larger than 20 something spaces, the first X w/
1404 // always be 0, and the the exponential value will only
1405 // be 2 digits long. Also integer types w/ always
1406 // have a positive exponential.
1409 private static string FormatExponential (byte value, int precision, bool upper)
1411 return FormatExponential ((uint)value, precision, upper);
1414 private static string FormatExponential (short value, int precision, bool upper)
1416 return FormatExponential ((int)value, precision, upper);
1419 private static string FormatExponential (int value, int precision, bool upper)
1421 bool negative = (value < 0);
1422 int padding = (precision >= 0) ? precision : 6;
1423 char[] buffy = new char[(padding + 8)];
1424 char[] tmp = new char [maxIntLength];
1425 int exponent = 0, position = maxIntLength;
1426 int exp = 0, idx = 0;
1429 // ugly, but doing it since abs(Int32.MinValue) > Int.MaxValue
1430 uint number = (negative) ? (uint)((-(value + 1)) + 1) : (uint)value;
1432 // need to calculate the number of places to know if we need to round later
1433 if (negative && value <= -10) {
1438 while (value >= 10) {
1443 if (exp > padding) {
1445 // highest number we should goto before we round
1446 while (idx++ <= padding)
1449 // get our value into a buffer
1450 while (number > pow) {
1451 tmp[--position] = digitLowerTable[(number % 10)];
1459 while (number >= 10) {
1460 tmp[--position] = digitLowerTable[(number% 10)];
1465 tmp[--position] = digitLowerTable[number];
1468 // go left to right in filling up new string
1472 // we know we have at least one in there, followed
1473 // by a decimal point
1474 buffy[idx++] = tmp[position++];
1478 // copy over the remaining digits until we run out,
1479 // or we've passed our specified precision
1480 while (padding > 0 && position < maxIntLength) {
1481 buffy[idx++] = tmp[position++];
1485 // if we still have more precision to go, add some
1487 while (padding > 0) {
1492 // we know these next 3 spots
1493 buffy[idx++] = upper ? 'E' : 'e';
1497 // next two digits depend on our length
1498 if (exponent >= 10) {
1499 buffy[idx++] = digitLowerTable[(exponent / 10)];
1500 buffy[idx] = digitLowerTable[(exponent % 10)];
1503 buffy[idx] = digitLowerTable[exponent];
1506 return new string(buffy, 0, ++idx);
1509 private static string FormatExponential (long value, int precision, bool upper)
1511 bool negative = (value < 0);
1512 int padding = (precision >= 0) ? precision : 6;
1513 char[] buffy = new char[(padding + 8)];
1514 char[] tmp = new char [maxLongLength];
1515 int exponent = 0, position = maxLongLength;
1516 int exp = 0, idx = 0;
1519 // ugly, but doing it since abs(Int32.MinValue) > Int.MaxValue
1520 ulong number = (negative) ? (ulong)((-(value + 1)) + 1) : (ulong)value;
1522 // need to calculate the number of places to know if we need to round later
1523 if (negative && value <= -10) {
1528 while (value >= 10) {
1533 if (exp > padding) {
1535 // highest number we should goto before we round
1536 while (idx++ <= padding)
1539 // get our value into a buffer
1540 while (number > pow) {
1541 tmp[--position] = digitLowerTable[(number % 10)];
1549 while (number >= 10) {
1550 tmp[--position] = digitLowerTable[(number% 10)];
1555 tmp[--position] = digitLowerTable[number];
1558 // go left to right in filling up new string
1562 // we know we have at least one in there, followed
1563 // by a decimal point
1564 buffy[idx++] = tmp[position++];
1568 // copy over the remaining digits until we run out,
1569 // or we've passed our specified precision
1570 while (padding > 0 && position < maxLongLength) {
1571 buffy[idx++] = tmp[position++];
1575 // if we still have more precision to go, add some
1577 while (padding > 0) {
1582 // we know these next 3 spots
1583 buffy[idx++] = upper ? 'E' : 'e';
1587 // next two digits depend on our length
1588 if (exponent >= 10) {
1589 buffy[idx++] = digitLowerTable[(exponent / 10)];
1590 buffy[idx] = digitLowerTable[(exponent % 10)];
1593 buffy[idx] = digitLowerTable[exponent];
1596 return new string(buffy, 0, ++idx);
1599 private static string FormatExponential (sbyte value, int precision, bool upper)
1601 return FormatExponential ((int)value, precision, upper);
1604 private static string FormatExponential (ushort value, int precision, bool upper)
1606 return FormatExponential ((uint)value, precision, upper);
1609 private static string FormatExponential (uint value, int precision, bool upper)
1611 int padding = (precision >= 0) ? precision : 6;
1612 char[] buffy = new char[(padding + 8)];
1613 char[] tmp = new char [maxIntLength];
1614 int exponent = 0, position = maxIntLength;
1615 int exp = 0, idx = 0;
1617 ulong number = (ulong)value;
1619 // need to calculate the number of places to know if we need to round later
1620 while (value >= 10) {
1625 if (exp > padding) {
1627 // highest number we should goto before we round
1628 while (idx++ <= padding)
1631 // get our value into a buffer
1632 while (number > pow) {
1633 tmp[--position] = digitLowerTable[(number % 10)];
1641 while (number >= 10) {
1642 tmp[--position] = digitLowerTable[(number% 10)];
1647 tmp[--position] = digitLowerTable[number];
1650 // we know we have at least one in there, followed
1651 // by a decimal point
1652 buffy[idx++] = tmp[position++];
1656 // copy over the remaining digits until we run out,
1657 // or we've passed our specified precision
1658 while (padding > 0 && position < maxIntLength) {
1659 buffy[idx++] = tmp[position++];
1663 // if we still have more precision to go, add some
1665 while (padding > 0) {
1670 // we know these next 3 spots
1671 buffy[idx++] = upper ? 'E' : 'e';
1675 // next two digits depend on our length
1676 if (exponent >= 10) {
1677 buffy[idx++] = digitLowerTable[(exponent / 10)];
1678 buffy[idx] = digitLowerTable[(exponent % 10)];
1681 buffy[idx] = digitLowerTable[exponent];
1684 return new string(buffy, 0, ++idx);
1687 private static string FormatExponential (ulong value, int precision, bool upper)
1689 int padding = (precision >= 0) ? precision : 6;
1690 char[] buffy = new char[(padding + 8)];
1691 char[] tmp = new char [maxLongLength];
1692 int exponent = 0, position = maxLongLength;
1693 int exp = 0, idx = 0;
1695 ulong number = value;
1697 // need to calculate the number of places to know if we need to round later
1698 while (value >= 10) {
1703 if (exp > padding) {
1705 // highest number we should goto before we round
1706 while (idx++ <= padding)
1709 // get our value into a buffer
1710 while (number > pow) {
1711 tmp[--position] = digitLowerTable[(number % 10)];
1719 while (number >= 10) {
1720 tmp[--position] = digitLowerTable[(number% 10)];
1725 tmp[--position] = digitLowerTable[number];
1728 // we know we have at least one in there, followed
1729 // by a decimal point
1730 buffy[idx++] = tmp[position++];
1734 // copy over the remaining digits until we run out,
1735 // or we've passed our specified precision
1736 while (padding > 0 && position < maxLongLength) {
1737 buffy[idx++] = tmp[position++];
1741 // if we still have more precision to go, add some
1743 while (padding > 0) {
1748 // we know these next 3 spots
1749 buffy[idx++] = upper ? 'E' : 'e';
1753 // next two digits depend on our length
1754 if (exponent >= 10) {
1755 buffy[idx++] = digitLowerTable[(exponent / 10)];
1756 buffy[idx] = digitLowerTable[(exponent % 10)];
1759 buffy[idx] = digitLowerTable[exponent];
1762 return new string(buffy, 0, ++idx);
1765 // ============ Format Fixed Points ============ //
1768 // Used for strings in the following form "[-]M.DD...D"
1769 // At least one non-zero digit precedes the '.', w/ a
1770 // '-' before that if negative. Precision specifies number
1771 // of decimal places 'D' to go. If not given, use
1772 // NumberFormatInfo.NumbeDecimalDigits. Results are rounded
1775 // Fairly simple implementation for integral types. Going
1776 // from right to left, fill up precision number of 0's,
1777 // plop a . down, then go for our number.
1780 private static string FormatFixedPoint (byte value, int precision, NumberFormatInfo nfi)
1782 return FormatFixedPoint ((uint)value, precision, nfi);
1785 private static string FormatFixedPoint (short value, int precision, NumberFormatInfo nfi)
1787 return FormatFixedPoint ((int)value, precision, nfi);
1790 private static string FormatFixedPoint (int value, int precision, NumberFormatInfo nfi)
1792 int padding = (precision >= 0) ? (precision + maxIntLength) : (nfi.NumberDecimalDigits + maxIntLength);
1793 char[] buffy = new char[padding];
1794 int position = padding;
1795 bool negative = (value < 0);
1797 // fill up w/ precision # of 0's
1798 while (position > (maxIntLength - 1))
1799 buffy[--position] = '0';
1802 buffy[position--] = '.';
1806 buffy[position--] = digitLowerTable[-(value % 10)];
1807 value = value / -10;
1808 } else value = -value;
1810 // fill up w/ the value
1811 while (value >= 10) {
1812 buffy[position--] = digitLowerTable[(value % 10)];
1816 buffy[position] = digitLowerTable[value];
1819 buffy[--position] = '-';
1821 return new string (buffy, position, (padding - position));
1824 private static string FormatFixedPoint (long value, int precision, NumberFormatInfo nfi)
1826 int padding = (precision >= 0) ? (precision + maxLongLength) : (nfi.NumberDecimalDigits + maxLongLength);
1827 char[] buffy = new char[padding];
1828 int position = padding;
1829 bool negative = (value < 0);
1831 // fill up w/ precision # of 0's
1832 while (position > (maxLongLength - 1))
1833 buffy[--position] = '0';
1836 buffy[position--] = '.';
1840 buffy[position--] = digitLowerTable[-(value % 10)];
1841 value = value / -10;
1842 } else value = -value;
1844 // fill up w/ the value
1845 while (value >= 10) {
1846 buffy[position--] = digitLowerTable[(value % 10)];
1850 buffy[position] = digitLowerTable[value];
1853 buffy[--position] = '-';
1855 return new string (buffy, position, (padding - position));
1858 private static string FormatFixedPoint (sbyte value, int precision, NumberFormatInfo nfi)
1860 return FormatFixedPoint ((int)value, precision, nfi);
1863 private static string FormatFixedPoint (ushort value, int precision, NumberFormatInfo nfi)
1865 return FormatFixedPoint ((uint)value, precision, nfi);
1868 private static string FormatFixedPoint (uint value, int precision, NumberFormatInfo nfi)
1870 int padding = (precision >= 0) ? (precision + maxIntLength) : (nfi.NumberDecimalDigits + maxIntLength);
1871 char[] buffy = new char[padding];
1872 int position = padding;
1874 // fill up w/ precision # of 0's
1875 while (position > (maxIntLength - 1))
1876 buffy[--position] = '0';
1879 buffy[position--] = '.';
1881 // fill up w/ the value
1882 while (value >= 10) {
1883 buffy[position--] = digitLowerTable[(value % 10)];
1887 buffy[position] = digitLowerTable[value];
1889 return new string (buffy, position, (padding - position));
1892 private static string FormatFixedPoint (ulong value, int precision, NumberFormatInfo nfi)
1894 int padding = (precision >= 0) ? (precision + maxLongLength) : (nfi.NumberDecimalDigits + maxLongLength);
1895 char[] buffy = new char[padding];
1896 int position = padding;
1898 // fill up w/ precision # of 0's
1899 while (position > (maxLongLength - 1))
1900 buffy[--position] = '0';
1903 buffy[position--] = '.';
1905 // fill up w/ the value
1906 while (value >= 10) {
1907 buffy[position--] = digitLowerTable[(value % 10)];
1911 buffy[position] = digitLowerTable[value];
1913 return new string (buffy, position, (padding - position));
1916 // ============ Format General ============ //
1919 // Strings are formatted in either Fixed Point or Exponential
1920 // format. Results are rounded when needed. If no precision is
1921 // given, the defaults are:
1923 // short & ushort: 5
1930 // The value is formatted using fixed-point if exponent >= -4
1931 // and exponent < precision, where exponent is he exponenent of
1932 // the value in exponential format. The decimal point and trailing
1933 // zeros are removed when possible.
1935 // For all other values, exponential format is used. The case of
1936 // the format specifier determines whether 'e' or 'E' prefixes
1939 // In either case, the number of digits that appear in the result
1940 // (not including the exponent) will not exceed the value of the
1941 // precision. The result is rounded as needed.
1943 // Integral values are formatted using Fixed Point whenever
1944 // precision is omitted. (This actually doesn't make sense when
1945 // coupled w/ the 1st paragraph).
1947 // Okay, so the decimal point is removed along with any trailing
1948 // zeros. So, ignoring the last paragraph, we can consider an int
1949 // ToString() to format it w/ exponential format w/ a default
1950 // precision of 10, but since it will just be .00000000, it's
1954 internal static string FormatGeneral (byte value, int precision, NumberFormatInfo nfi, bool upper) {
1955 return FormatGeneral ((uint)value, precision, nfi, upper);
1958 internal static string FormatGeneral (short value, int precision, NumberFormatInfo nfi, bool upper) {
1959 return FormatGeneral ((int)value, precision, nfi, upper);
1962 internal static string FormatGeneral (int value, int precision, NumberFormatInfo nfi, bool upper)
1964 bool negative = (value < 0);
1965 char[] tmp = new char [maxIntLength];
1967 int position = maxIntLength;
1969 // ugly, but doing it since abs(Int32.MinValue) > Int.MaxValue
1970 uint number = (negative) ? (uint)((-(value + 1)) + 1) : (uint)value;
1972 // get number into a buffer, going to be doing this no matter what
1975 tmp[--position] = digitLowerTable[-(value % 10)];
1978 } else value = -value;
1980 while (value >= 10) {
1981 tmp[--position] = digitLowerTable[(value % 10)];
1986 tmp[--position] = digitLowerTable[value];
1988 // integral values are formatted using fixed point when precision
1989 // is not specified. But also trailing decimal point and zeros are
1990 // discared. So for int's it will always be .00, so just compute
1991 // here and save the call to FormatFixedPoint & trim.
1992 if (precision <= 0 || exponent < precision) {
1994 tmp[--position] = '-';
1996 return new string (tmp, position, (maxIntLength - position));
1999 // else our exponent was > precision, use exponential format
2000 // precision = number of digits to show.
2005 position = maxIntLength;
2007 // Loop through while our number is less than the 10 ^ precision, then
2008 // add 5 to that to round it out, and keep continuing
2009 while (idx++ <= precision)
2012 while (number > pow) {
2013 tmp[--position] = digitLowerTable[(number % 10)];
2020 while (number >= 10) {
2021 tmp[--position] = digitLowerTable[(number % 10)];
2026 tmp[--position] = digitLowerTable[number];
2028 // finally, make our final buffer, at least precision + 6 for 'E+XX' and '-'
2029 // and reuse pow for size
2032 pow = (ulong)(precision + 6);
2033 char[] buffy = new char[pow];
2036 buffy[position++] = '-';
2038 buffy[position++] = tmp[idx++];
2039 buffy[position] = '.';
2041 // for the remaining precisions copy over rounded tmp
2043 while (precision-- > 0)
2044 buffy[++position] = tmp[idx++];
2046 // get rid of ending zeros
2047 while (buffy[position] == '0')
2050 // if we backed up all the way to the ., over write it
2051 if (buffy[position] != '.')
2054 // ints can only be +, e or E depending on format, plus XX
2055 buffy[position++] = upper ? 'E' : 'e';
2056 buffy[position++] = '+';
2058 if (exponent >= 10) {
2059 buffy[position++] = digitLowerTable[(exponent / 10)];
2060 buffy[position++] = digitLowerTable[(exponent % 10)];
2062 buffy[position++] = '0';
2063 buffy[position++] = digitLowerTable[exponent];
2066 return new string (buffy, 0, position);
2069 internal static string FormatGeneral (long value, int precision, NumberFormatInfo nfi, bool upper)
2071 bool negative = (value < 0);
2072 char[] tmp = new char [maxLongLength];
2074 int position = maxLongLength;
2076 // ugly, but doing it since abs(Int32.MinValue) > Int.MaxValue
2077 ulong number = (negative) ? (ulong)(-(value + 1) + 1) : (ulong)value;
2079 // get number into a buffer, going to be doing this no matter what
2082 tmp[--position] = digitLowerTable[-(value % 10)];
2084 } else value = -value;
2086 while (value >= 10) {
2087 tmp[--position] = digitLowerTable[(value % 10)];
2091 tmp[--position] = digitLowerTable[value];
2092 exponent = (maxLongLength - position) - 1;
2094 // integral values are formatted using fixed point when precision
2095 // is not specified. But also trailing decimal point and zeros are
2096 // discared. So for int's it will always be .00, so just compute
2097 // here and save the call to FormatFixedPoint & trim.
2098 if (precision <= 0 || exponent < precision) {
2100 tmp[--position] = '-';
2102 return new string (tmp, position, (maxLongLength - position));
2105 // else our exponent was > precision, use exponential format
2106 // precision = number of digits to show.
2111 position = maxLongLength;
2113 // Loop through while our number is less than the 10 ^ precision, then
2114 // add 5 to that to round it out, and keep continuing
2115 while (idx++ <= precision)
2118 while (number > pow) {
2119 tmp[--position] = digitLowerTable[(number % 10)];
2126 while (number >= 10) {
2127 tmp[--position] = digitLowerTable[(number % 10)];
2132 tmp[--position] = digitLowerTable[number];
2134 // finally, make our final buffer, at least precision + 6 for 'E+XX' and '-'
2135 // and reuse pow for size
2138 pow = (ulong)precision + 6;
2139 char[] buffy = new char[pow];
2142 buffy[position++] = '-';
2144 buffy[position++] = tmp[idx++];
2145 buffy[position] = '.';
2147 // for the remaining precisions copy over rounded tmp
2149 while (precision-- > 0)
2150 buffy[++position] = tmp[idx++];
2152 // get rid of ending zeros
2153 while (buffy[position] == '0')
2156 // if we backed up all the way to the ., over write it
2157 if (buffy[position] != '.')
2160 // ints can only be +, e or E depending on format, plus XX
2161 buffy[position++] = upper ? 'E' : 'e';
2162 buffy[position++] = '+';
2164 if (exponent >= 10) {
2165 buffy[position++] = digitLowerTable[(exponent / 10)];
2166 buffy[position++] = digitLowerTable[(exponent % 10)];
2168 buffy[position++] = '0';
2169 buffy[position++] = digitLowerTable[exponent];
2172 return new string (buffy, 0, position);
2175 internal static string FormatGeneral (sbyte value, int precision, NumberFormatInfo nfi, bool upper) {
2176 return FormatGeneral ((int)value, precision, nfi, upper);
2179 internal static string FormatGeneral (ushort value, int precision, NumberFormatInfo nfi, bool upper) {
2180 return FormatGeneral ((uint)value, precision, nfi, upper);
2183 internal static string FormatGeneral (uint value, int precision, NumberFormatInfo nfi, bool upper)
2185 char[] tmp = new char [maxIntLength];
2187 int position = maxIntLength;
2188 ulong number = (ulong)value;
2190 // get number into a buffer, going to be doing this no matter what
2191 while (value >= 10) {
2192 tmp[--position] = digitLowerTable[(value % 10)];
2196 tmp[--position] = digitLowerTable[value];
2197 exponent = (maxIntLength - position) - 1;
2199 // integral values are formatted using fixed point when precision
2200 // is not specified. But also trailing decimal point and zeros are
2201 // discared. So for int's it will always be .00, so just compute
2202 // here and save the call to FormatFixedPoint & trim.
2203 if (precision <= 0 || exponent < precision)
2204 return new string (tmp, position, (maxIntLength - position));
2206 // else our exponent was > precision, use exponential format
2207 // precision = number of digits to show.
2212 position = maxIntLength;
2214 // Loop through while our number is less than the 10 ^ precision, then
2215 // add 5 to that to round it out, and keep continuing
2216 while (idx++ <= precision)
2219 while (number > pow) {
2220 tmp[--position] = digitLowerTable[(number % 10)];
2227 while (number >= 10) {
2228 tmp[--position] = digitLowerTable[(number % 10)];
2233 tmp[--position] = digitLowerTable[number];
2235 // finally, make our final buffer, at least precision + 6 for 'E+XX' and '-'
2236 // and reuse pow for size
2239 pow = (ulong)(precision + 6);
2240 char[] buffy = new char[pow];
2242 buffy[position++] = tmp[idx++];
2243 buffy[position] = '.';
2245 // for the remaining precisions copy over rounded tmp
2247 while (precision-- > 0)
2248 buffy[++position] = tmp[idx++];
2250 // get rid of ending zeros
2251 while (buffy[position] == '0')
2254 // if we backed up all the way to the ., over write it
2255 if (buffy[position] != '.')
2258 // ints can only be +, e or E depending on format, plus XX
2259 buffy[position++] = upper ? 'E' : 'e';
2260 buffy[position++] = '+';
2262 if (exponent >= 10) {
2263 buffy[position++] = digitLowerTable[(exponent / 10)];
2264 buffy[position++] = digitLowerTable[(exponent % 10)];
2266 buffy[position++] = '0';
2267 buffy[position++] = digitLowerTable[exponent];
2270 return new string (buffy, 0, position);
2273 internal static string FormatGeneral (ulong value, int precision, NumberFormatInfo nfi, bool upper)
2275 char[] tmp = new char [maxLongLength];
2277 int position = maxLongLength;
2278 ulong number = value;
2280 // get number into a buffer, going to be doing this no matter what
2281 while (value >= 10) {
2282 tmp[--position] = digitLowerTable[(value % 10)];
2287 tmp[--position] = digitLowerTable[value];
2289 // integral values are formatted using fixed point when precision
2290 // is not specified. But also trailing decimal point and zeros are
2291 // discared. So for int's it will always be .00, so just compute
2292 // here and save the call to FormatFixedPoint & trim.
2293 if (precision <= 0 || exponent < precision)
2294 return new string (tmp, position, (maxLongLength - position));
2296 // else our exponent was > precision, use exponential format
2297 // precision = number of digits to show.
2302 position = maxLongLength;
2304 // Loop through while our number is less than the 10 ^ precision, then
2305 // add 5 to that to round it out, and keep continuing
2306 while (idx++ <= precision)
2309 while (number > pow) {
2310 tmp[--position] = digitLowerTable[(number % 10)];
2317 while (number >= 10) {
2318 tmp[--position] = digitLowerTable[(number % 10)];
2323 tmp[--position] = digitLowerTable[number];
2325 // finally, make our final buffer, at least precision + 6 for 'E+XX' and '-'
2326 // and reuse pow for size
2329 pow = (ulong)precision + 6;
2330 char[] buffy = new char[pow];
2332 buffy[position++] = tmp[idx++];
2333 buffy[position] = '.';
2335 // for the remaining precisions copy over rounded tmp
2337 while (precision-- > 0)
2338 buffy[++position] = tmp[idx++];
2340 // get rid of ending zeros
2341 while (buffy[position] == '0')
2344 // if we backed up all the way to the ., over write it
2345 if (buffy[position] != '.')
2348 // ints can only be +, e or E depending on format, plus XX
2349 buffy[position++] = upper ? 'E' : 'e';
2350 buffy[position++] = '+';
2352 if (exponent >= 10) {
2353 buffy[position++] = digitLowerTable[(exponent / 10)];
2354 buffy[position++] = digitLowerTable[(exponent % 10)];
2356 buffy[position++] = '0';
2357 buffy[position++] = digitLowerTable[exponent];
2360 return new string (buffy, 0, position);
2363 // ============ Format Number ============ //
2366 // Used for strings in the following form "[-]d,ddd,ddd.dd...d"
2367 // The minus sign only appears if it is negative. At least one
2368 // non-zero digit preceeds the decimal separator. The precision
2369 // specifier determines the number of decimal places. If it is
2370 // not given, use NumberFormatInfo.NumberDecimalDigits.
2371 // The NumberGroupSizes, NumberGroupSeparator, and NumberDecimalSeparator
2372 // members of NumberFormatInfo supply the size and separator
2373 // for digit groupings. See IFormattable.
2375 // The group sizes is an array of ints that determine the grouping
2376 // of numbers. All digits are in the range 1-9, with the last digit
2377 // being between 0-9. The number formats the string backwards, with
2378 // the last digit being the group size for the rest of (leftmost) the
2379 // the string, 0 being none.
2382 // groupSizes = { 3, 2, 1, 0 };
2383 // int n = 1234567890 => "1234,5,67,890"
2384 // groupSizes = { 3, 2, 1 };
2385 // int n = 1234567890 => "1,2,3,4,5,67,890"
2386 // groupSizes = { 2, 0 };
2387 // int n = 1234567890 => "1234567,90";
2389 // Not too difficult, jsut keep track of where you are in the array
2390 // and when to print the separator
2392 // The max size of the buffer is assume we have a separator every
2393 // number, plus the precision on the end, plus a spot for the negative
2394 // and a spot for decimal separator.
2397 private static string FormatNumber (byte value, int precision, NumberFormatInfo nfi) {
2398 return FormatNumber ((uint)value, precision, nfi);
2401 private static string FormatNumber (short value, int precision, NumberFormatInfo nfi) {
2402 return FormatNumber ((int)value, precision, nfi);
2405 private static string FormatNumber (int value, int precision, NumberFormatInfo nfi)
2408 char[] groupSeparator = nfi.NumberGroupSeparator.ToCharArray ();
2409 char[] decimalSeparator = nfi.NumberDecimalSeparator.ToCharArray ();
2410 int[] groupSizes = nfi.NumberGroupSizes;
2412 int padding = (precision >= 0) ? precision : nfi.NumberDecimalDigits;
2413 int pattern = nfi.NumberNegativePattern;
2414 int size = maxIntLength + (maxIntLength * groupSeparator.Length) + padding +
2415 decimalSeparator.Length + 4;
2416 char[] buffy = new char[size];
2417 int position = size;
2418 bool negative = (value < 0);
2420 // pattern for negative values, defined in NumberFormatInfo
2424 buffy[--position] = ')';
2431 buffy[--position] = '-';
2434 buffy[--position] = '-';
2435 buffy[--position] = ' ';
2440 // right pad it w/ precision 0's
2441 while (padding-- > 0)
2442 buffy[--position] = '0';
2444 // put on decimal separator
2445 if (position != size) {
2446 i = decimalSeparator.Length;
2448 buffy[--position] = decimalSeparator[--i];
2452 // loop through, keeping track of where you are in the
2453 // group sizes array and putting out the group separator
2456 k = groupSizes[j++];
2458 // negative hack for numbers past MinValue
2461 buffy[--position] = digitLowerTable[-(value % 10)];
2462 value = value / -10;
2465 i = groupSeparator.Length;
2467 buffy[--position] = groupSeparator[--i];
2470 k = (j < groupSizes.Length) ?
2471 groupSizes[j++] : groupSizes[(groupSizes.Length - 1)];
2473 } else value = -value;
2475 while (value >= 10) {
2476 buffy[--position] = digitLowerTable[(value % 10)];
2480 i = groupSeparator.Length;
2482 buffy[--position] = groupSeparator[--i];
2485 k = (j < groupSizes.Length) ?
2486 groupSizes[j++] : groupSizes[(groupSizes.Length - 1)];
2490 buffy[--position] = digitLowerTable[value];
2492 // pattern for negative values, defined in NumberFormatInfo
2496 buffy[--position] = '(';
2499 buffy[--position] = '-';
2502 buffy[--position] = ' ';
2503 buffy[--position] = '-';
2512 return new string (buffy, position, (size - position));
2515 private static string FormatNumber (long value, int precision, NumberFormatInfo nfi)
2518 char[] groupSeparator = nfi.NumberGroupSeparator.ToCharArray ();
2519 char[] decimalSeparator = nfi.NumberDecimalSeparator.ToCharArray ();
2520 int[] groupSizes = nfi.NumberGroupSizes;
2522 int padding = (precision >= 0) ? precision : nfi.NumberDecimalDigits;
2523 int pattern = nfi.NumberNegativePattern;
2524 int size = maxLongLength + (maxLongLength * groupSeparator.Length) + padding +
2525 decimalSeparator.Length + 4;
2526 char[] buffy = new char[size];
2527 int position = size;
2528 bool negative = (value < 0);
2530 // pattern for negative values, defined in NumberFormatInfo
2534 buffy[--position] = ')';
2541 buffy[--position] = '-';
2544 buffy[--position] = '-';
2545 buffy[--position] = ' ';
2550 // right pad it w/ precision 0's
2551 while (padding-- > 0)
2552 buffy[--position] = '0';
2554 // put on decimal separator
2555 if (position != size) {
2556 i = decimalSeparator.Length;
2558 buffy[--position] = decimalSeparator[--i];
2562 // loop through, keeping track of where you are in the
2563 // group sizes array and putting out the group separator
2566 k = groupSizes[j++];
2568 // negative hack for numbers past MinValue
2571 buffy[--position] = digitLowerTable[-(value % 10)];
2572 value = value / -10;
2575 i = groupSeparator.Length;
2577 buffy[--position] = groupSeparator[--i];
2580 k = (j < groupSizes.Length) ?
2581 groupSizes[j++] : groupSizes[(groupSizes.Length - 1)];
2583 } else value = -value;
2585 while (value >= 10) {
2586 buffy[--position] = digitLowerTable[(value % 10)];
2590 i = groupSeparator.Length;
2592 buffy[--position] = groupSeparator[--i];
2595 k = (j < groupSizes.Length) ?
2596 groupSizes[j++] : groupSizes[(groupSizes.Length - 1)];
2600 buffy[--position] = digitLowerTable[value];
2602 // pattern for negative values, defined in NumberFormatInfo
2606 buffy[--position] = '(';
2609 buffy[--position] = '-';
2612 buffy[--position] = ' ';
2613 buffy[--position] = '-';
2622 return new string (buffy, position, (size - position));
2625 private static string FormatNumber (sbyte value, int precision, NumberFormatInfo nfi) {
2626 return FormatNumber ((int)value, precision, nfi);
2629 private static string FormatNumber (ushort value, int precision, NumberFormatInfo nfi) {
2630 return FormatNumber ((uint)value, precision, nfi);
2633 private static string FormatNumber (uint value, int precision, NumberFormatInfo nfi)
2636 char[] groupSeparator = nfi.NumberGroupSeparator.ToCharArray ();
2637 char[] decimalSeparator = nfi.NumberDecimalSeparator.ToCharArray ();
2638 int[] groupSizes = nfi.NumberGroupSizes;
2640 int padding = (precision >= 0) ? precision : nfi.NumberDecimalDigits;
2641 int size = maxIntLength + (maxIntLength * groupSeparator.Length) + padding +
2642 decimalSeparator.Length + 2;
2643 char[] buffy = new char[size];
2644 int position = size;
2646 // right pad it w/ precision 0's
2647 while (padding-- > 0)
2648 buffy[--position] = '0';
2650 // put on decimal separator
2651 if (position != size) {
2652 i = decimalSeparator.Length;
2654 buffy[--position] = decimalSeparator[--i];
2658 // loop through, keeping track of where you are in the
2659 // group sizes array and putting out the group separator
2662 k = groupSizes[j++];
2664 while (value >= 10) {
2665 buffy[--position] = digitLowerTable[(value % 10)];
2669 i = groupSeparator.Length;
2671 buffy[--position] = groupSeparator[--i];
2674 k = (j < groupSizes.Length) ?
2675 groupSizes[j++] : groupSizes[(groupSizes.Length - 1)];
2679 buffy[--position] = digitLowerTable[value];
2681 return new string (buffy, position, (size - position));
2684 private static string FormatNumber (ulong value, int precision, NumberFormatInfo nfi)
2687 char[] groupSeparator = nfi.NumberGroupSeparator.ToCharArray ();
2688 char[] decimalSeparator = nfi.NumberDecimalSeparator.ToCharArray ();
2689 int[] groupSizes = nfi.NumberGroupSizes;
2691 int padding = (precision >= 0) ? precision : nfi.NumberDecimalDigits;
2692 int size = maxLongLength + (maxLongLength * groupSeparator.Length) + padding +
2693 decimalSeparator.Length + 2;
2694 char[] buffy = new char[size];
2695 int position = size;
2697 // right pad it w/ precision 0's
2698 while (padding-- > 0)
2699 buffy[--position] = '0';
2701 // put on decimal separator
2702 if (position != size) {
2703 i = decimalSeparator.Length;
2705 buffy[--position] = decimalSeparator[--i];
2709 // loop through, keeping track of where you are in the
2710 // group sizes array and putting out the group separator
2713 k = groupSizes[j++];
2715 while (value >= 10) {
2716 buffy[--position] = digitLowerTable[(value % 10)];
2720 i = groupSeparator.Length;
2722 buffy[--position] = groupSeparator[--i];
2725 k = (j < groupSizes.Length) ?
2726 groupSizes[j++] : groupSizes[(groupSizes.Length - 1)];
2730 buffy[--position] = digitLowerTable[value];
2732 return new string (buffy, position, (size - position));
2735 // ============ Percent Formatting ============ //
2738 // Percent Format: Used for strings containing a percentage. The
2739 // PercentSymbol, PercentGroupSizes, PercentGroupSeparator, and
2740 // PercentDecimalSeparator members of a NumberFormatInfo supply
2741 // the Percent symbol, size and separator for digit groupings, and
2742 // decimal separator, respectively.
2743 // PercentNegativePattern and PercentPositivePattern determine the
2744 // symbols used to represent negative and positive values. For example,
2745 // a negative value may be prefixed with a minus sign, or enclosed in
2747 // If no precision is specified, the number of decimal places in the result
2748 // is set by NumberFormatInfo.PercentDecimalDigits. Results are
2749 // rounded to the nearest representable value when necessary.
2750 // The result is scaled by 100 (.99 becomes 99%).
2752 // The pattern of the number determines how the output looks, where
2753 // the percent sign goes, where the negative sign goes, etc.
2754 // IFormattable documentation lists the patterns and their values,
2755 // I have them commented out in the switch statement
2758 private static string FormatPercent (byte value, int precision, NumberFormatInfo nfi)
2760 return FormatPercent ((uint)value, precision, nfi);
2763 private static string FormatPercent (short value, int precision, NumberFormatInfo nfi)
2765 return FormatPercent ((int)value, precision, nfi);
2768 private static string FormatPercent (int value, int precision, NumberFormatInfo nfi)
2771 bool negative = (value < 0);
2773 char[] groupSeparator = nfi.PercentGroupSeparator.ToCharArray ();
2774 char[] decimalSeparator = nfi.PercentDecimalSeparator.ToCharArray ();
2775 char[] percentSymbol = nfi.PercentSymbol.ToCharArray ();
2776 int[] groupSizes = nfi.PercentGroupSizes;
2777 int pattern = negative ? nfi.PercentNegativePattern : nfi.PercentPositivePattern;
2778 int symbolLength = percentSymbol.Length;
2780 int padding = (precision >= 0) ? precision : nfi.PercentDecimalDigits;
2781 int size = maxIntLength + (groupSeparator.Length * maxIntLength) + padding + 2 +
2782 decimalSeparator.Length + symbolLength;
2783 char[] buffy = new char[size];
2784 int position = size;
2786 // set up the pattern from IFormattible
2793 buffy[--position] = percentSymbol[--i];
2795 buffy[--position] = ' ';
2799 buffy[--position] = percentSymbol[--i];
2810 buffy[--position] = percentSymbol[--i];
2812 buffy[--position] = ' ';
2816 buffy[--position] = percentSymbol[--i];
2824 // right pad it w/ precision 0's
2825 while (padding-- > 0)
2826 buffy[--position] = '0';
2828 // put on decimal separator if we moved over and put a 0
2829 if (position < size && buffy[position] == '0') {
2830 i = decimalSeparator.Length;
2832 buffy[--position] = decimalSeparator[--i];
2836 // loop through, keeping track of where you are in the
2837 // group sizes array and putting out the group separator
2840 k = groupSizes[j++];
2842 // all values are multiplied by 100, so tack on two 0's
2844 for (int c = 0; c < 2; c++) {
2845 buffy[--position] = '0';
2848 i = groupSeparator.Length;
2850 buffy[--position] = groupSeparator[--i];
2853 k = (j < groupSizes.Length) ?
2854 groupSizes[j++] : groupSizes[(groupSizes.Length - 1)];
2858 // negative hack for numbers past MinValue
2861 buffy[--position] = digitLowerTable[-(value % 10)];
2862 value = value / -10;
2865 i = groupSeparator.Length;
2867 buffy[--position] = groupSeparator[--i];
2870 k = (j < groupSizes.Length) ?
2871 groupSizes[j++] : groupSizes[(groupSizes.Length - 1)];
2873 } else value = -value;
2875 while (value >= 10) {
2876 buffy[--position] = digitLowerTable[(value % 10)];
2880 i = groupSeparator.Length;
2882 buffy[--position] = groupSeparator[--i];
2885 k = (j < groupSizes.Length) ?
2886 groupSizes[j++] : groupSizes[(groupSizes.Length - 1)];
2890 buffy[--position] = digitLowerTable[value];
2892 // end the pattern on the left hand side
2898 buffy[--position] = '-';
2901 buffy[--position] = '-';
2905 buffy[--position] = percentSymbol[--i];
2907 buffy[--position] = '-';
2919 buffy[--position] = percentSymbol[--i];
2925 return new string (buffy, position, (size - position));
2928 private static string FormatPercent (long value, int precision, NumberFormatInfo nfi)
2931 bool negative = (value < 0);
2933 char[] groupSeparator = nfi.PercentGroupSeparator.ToCharArray ();
2934 char[] decimalSeparator = nfi.PercentDecimalSeparator.ToCharArray ();
2935 char[] percentSymbol = nfi.PercentSymbol.ToCharArray ();
2936 int[] groupSizes = nfi.PercentGroupSizes;
2937 int pattern = negative ? nfi.PercentNegativePattern : nfi.PercentPositivePattern;
2938 int symbolLength = percentSymbol.Length;
2940 int padding = (precision >= 0) ? precision : nfi.PercentDecimalDigits;
2941 int size = maxLongLength + (groupSeparator.Length * maxLongLength) + padding + 2 +
2942 decimalSeparator.Length + symbolLength;
2943 char[] buffy = new char[size];
2944 int position = size;
2946 // set up the pattern from IFormattible
2953 buffy[--position] = percentSymbol[--i];
2955 buffy[--position] = ' ';
2959 buffy[--position] = percentSymbol[--i];
2970 buffy[--position] = percentSymbol[--i];
2972 buffy[--position] = ' ';
2976 buffy[--position] = percentSymbol[--i];
2984 // right pad it w/ precision 0's
2985 while (padding-- > 0)
2986 buffy[--position] = '0';
2988 // put on decimal separator if we moved over and put a 0
2989 if (position < size && buffy[position] == '0') {
2990 i = decimalSeparator.Length;
2992 buffy[--position] = decimalSeparator[--i];
2996 // loop through, keeping track of where you are in the
2997 // group sizes array and putting out the group separator
3000 k = groupSizes[j++];
3002 // all values are multiplied by 100, so tack on two 0's
3004 for (int c = 0; c < 2; c++) {
3005 buffy[--position] = '0';
3008 i = groupSeparator.Length;
3010 buffy[--position] = groupSeparator[--i];
3013 k = (j < groupSizes.Length) ?
3014 groupSizes[j++] : groupSizes[(groupSizes.Length - 1)];
3018 // negative hack for numbers past MinValue
3021 buffy[--position] = digitLowerTable[-(value % 10)];
3022 value = value / -10;
3025 i = groupSeparator.Length;
3027 buffy[--position] = groupSeparator[--i];
3030 k = (j < groupSizes.Length) ?
3031 groupSizes[j++] : groupSizes[(groupSizes.Length - 1)];
3033 } else value = -value;
3035 while (value >= 10) {
3036 buffy[--position] = digitLowerTable[(value % 10)];
3040 i = groupSeparator.Length;
3042 buffy[--position] = groupSeparator[--i];
3045 k = (j < groupSizes.Length) ?
3046 groupSizes[j++] : groupSizes[(groupSizes.Length - 1)];
3050 buffy[--position] = digitLowerTable[value];
3052 // end the pattern on the left hand side
3058 buffy[--position] = '-';
3061 buffy[--position] = '-';
3065 buffy[--position] = percentSymbol[--i];
3067 buffy[--position] = '-';
3079 buffy[--position] = percentSymbol[--i];
3085 return new string (buffy, position, (size - position));
3088 private static string FormatPercent (sbyte value, int precision, NumberFormatInfo nfi)
3090 return FormatPercent ((int)value, precision, nfi);
3093 private static string FormatPercent (ushort value, int precision, NumberFormatInfo nfi)
3095 return FormatPercent ((uint)value, precision, nfi);
3098 private static string FormatPercent (uint value, int precision, NumberFormatInfo nfi)
3102 char[] groupSeparator = nfi.PercentGroupSeparator.ToCharArray ();
3103 char[] decimalSeparator = nfi.PercentDecimalSeparator.ToCharArray ();
3104 char[] percentSymbol = nfi.PercentSymbol.ToCharArray ();
3105 int[] groupSizes = nfi.PercentGroupSizes;
3106 int pattern = nfi.PercentPositivePattern;
3107 int symbolLength = percentSymbol.Length;
3109 int padding = (precision >= 0) ? precision : nfi.PercentDecimalDigits;
3110 int size = maxIntLength + (groupSeparator.Length * maxIntLength) + padding + 2 +
3111 decimalSeparator.Length + symbolLength;
3112 char[] buffy = new char[size];
3113 int position = size;
3115 // set up the pattern from IFormattible
3120 buffy[--position] = percentSymbol[--i];
3122 buffy[--position] = ' ';
3126 buffy[--position] = percentSymbol[--i];
3133 // right pad it w/ precision 0's
3134 while (padding-- > 0)
3135 buffy[--position] = '0';
3137 // put on decimal separator if we moved over and put a 0
3138 if (position < size && buffy[position] == '0') {
3139 i = decimalSeparator.Length;
3141 buffy[--position] = decimalSeparator[--i];
3145 // loop through, keeping track of where you are in the
3146 // group sizes array and putting out the group separator
3149 k = groupSizes[j++];
3152 for (int c = 0; c < 2; c++) {
3153 buffy[--position] = '0';
3156 i = groupSeparator.Length;
3158 buffy[--position] = groupSeparator[--i];
3161 k = (j < groupSizes.Length) ?
3162 groupSizes[j++] : groupSizes[(groupSizes.Length - 1)];
3166 while (value >= 10) {
3167 buffy[--position] = digitLowerTable[(value % 10)];
3171 i = groupSeparator.Length;
3173 buffy[--position] = groupSeparator[--i];
3176 k = (j < groupSizes.Length) ?
3177 groupSizes[j++] : groupSizes[(groupSizes.Length - 1)];
3181 buffy[--position] = digitLowerTable[value];
3191 buffy[--position] = percentSymbol[--i];
3196 return new string (buffy, position, (size - position));
3199 private static string FormatPercent (ulong value, int precision, NumberFormatInfo nfi)
3203 char[] groupSeparator = nfi.PercentGroupSeparator.ToCharArray ();
3204 char[] decimalSeparator = nfi.PercentDecimalSeparator.ToCharArray ();
3205 char[] percentSymbol = nfi.PercentSymbol.ToCharArray ();
3206 int[] groupSizes = nfi.PercentGroupSizes;
3207 int pattern = nfi.PercentPositivePattern;
3208 int symbolLength = percentSymbol.Length;
3210 int padding = (precision >= 0) ? precision : nfi.PercentDecimalDigits;
3211 int size = maxLongLength + (groupSeparator.Length * maxLongLength) + padding + 2 +
3212 decimalSeparator.Length + symbolLength;
3213 char[] buffy = new char[size];
3214 int position = size;
3216 // set up the pattern from IFormattible
3221 buffy[--position] = percentSymbol[--i];
3223 buffy[--position] = ' ';
3227 buffy[--position] = percentSymbol[--i];
3234 // right pad it w/ precision 0's
3235 while (padding-- > 0)
3236 buffy[--position] = '0';
3238 // put on decimal separator if we moved over and put a 0
3239 if (position < size && buffy[position] == '0') {
3240 i = decimalSeparator.Length;
3242 buffy[--position] = decimalSeparator[--i];
3246 // loop through, keeping track of where you are in the
3247 // group sizes array and putting out the group separator
3250 k = groupSizes[j++];
3253 for (int c = 0; c < 2; c++) {
3254 buffy[--position] = '0';
3257 i = groupSeparator.Length;
3259 buffy[--position] = groupSeparator[--i];
3262 k = (j < groupSizes.Length) ?
3263 groupSizes[j++] : groupSizes[(groupSizes.Length - 1)];
3267 while (value >= 10) {
3268 buffy[--position] = digitLowerTable[(value % 10)];
3272 i = groupSeparator.Length;
3274 buffy[--position] = groupSeparator[--i];
3277 k = (j < groupSizes.Length) ?
3278 groupSizes[j++] : groupSizes[(groupSizes.Length - 1)];
3282 buffy[--position] = digitLowerTable[value];
3292 buffy[--position] = percentSymbol[--i];
3297 return new string (buffy, position, (size - position));
3300 // ============ Format Hexadecimal ============ //
3303 // For strings in base 16. Only valid w/ integers. Precision
3304 // specifies number of digits in the string, if it specifies
3305 // more digits than we need, left pad w/ 0's. The case of the
3306 // the format specifier 'X' or 'x' determines lowercase or
3307 // capital digits in the output.
3309 // Whew. Straight forward Hex formatting, however only
3310 // go 8 places max when dealing with an int (not counting
3311 // precision padding) and 16 when dealing with a long. This
3312 // is to cut off the loop when dealing with negative values,
3313 // which will loop forever when you hit -1;
3316 private static string FormatHexadecimal (byte value, int precision, bool upper)
3318 if (precision < 0) precision = 0;
3319 int size = maxByteLength + precision;
3320 char[] buffy = new char[size];
3321 char[] table = upper ? digitUpperTable : digitLowerTable;
3322 int position = size;
3323 ushort mask = (1 << 4) - 1;
3325 // loop through right to left, shifting and looking up
3326 // our value. Don't worry about negative
3328 buffy[--position] = table[(value & mask)];
3329 value = (byte)(value >> 4);
3330 } while (value != 0);
3332 // pad w/ 0's if they want more length, if not, ignore
3333 precision -= (size - position);
3334 while (precision > 0 && position > 1) {
3335 buffy[--position] = '0';
3339 return new string(buffy, position, (size - position));
3342 private static string FormatHexadecimal (short value, int precision, bool upper)
3344 if (precision < 0) precision = 0;
3345 int size = maxShortLength + precision;
3346 char[] buffy = new char[size];
3347 char[] table = upper ? digitUpperTable : digitLowerTable;
3348 int position = size;
3349 short mask = (1 << 4) - 1;
3351 // loop through right to left, shifting and looking up
3352 // our value. If value is negavite stop after 4 F's
3354 buffy[--position] = table[(value & mask)];
3355 value = (short)(value >> 4);
3356 } while (value != 0 && position > (size - 4));
3358 // pad w/ 0's if they want more length, if not, ignore
3359 precision -= (size - position);
3360 while (precision > 0 && position > 1) {
3361 buffy[--position] = '0';
3365 return new string(buffy, position, (size - position));
3368 private static string FormatHexadecimal (int value, int precision, bool upper)
3370 if (precision < 0) precision = 0;
3371 int size = maxIntLength + precision;
3372 char[] buffy = new char[size];
3373 char[] table = upper ? digitUpperTable : digitLowerTable;
3374 int position = size;
3375 int mask = (1 << 4) - 1;
3377 // loop through right to left, shifting and looking up
3378 // our value. If value is negavite stop after 8 F's
3380 buffy[--position] = table[(value & mask)];
3382 } while (value != 0 && position > (size - 8));
3384 // pad w/ 0's if they want more length, if not, ignore
3385 precision -= (size - position);
3386 while (precision > 0 && position > 1) {
3387 buffy[--position] = '0';
3391 return new string(buffy, position, (size - position));
3394 private static string FormatHexadecimal (long value, int precision, bool upper)
3396 if (precision < 0) precision = 0;
3397 int size = maxLongLength + precision;
3398 char[] buffy = new char[size];
3399 char[] table = upper ? digitUpperTable : digitLowerTable;
3400 int position = size;
3401 long mask = (1 << 4) - 1;
3403 // loop through right to left, shifting and looking up
3404 // our value. If value is negavite stop after 16 F's
3406 buffy[--position] = table[(value & mask)];
3408 } while (value != 0 && position > (size - 16));
3410 // pad w/ 0's if they want more length, if not, ignore
3411 precision -= (size - position);
3412 while (precision > 0 && position > 1) {
3413 buffy[--position] = '0';
3417 return new string(buffy, position, (size - position));
3420 private static string FormatHexadecimal (sbyte value, int precision, bool upper)
3422 if (precision < 0) precision = 0;
3423 int size = maxByteLength + precision;
3424 char[] buffy = new char[size];
3425 char[] table = upper ? digitUpperTable : digitLowerTable;
3426 int position = size;
3427 short mask = (1 << 4) - 1;
3429 // loop through right to left, shifting and looking up
3430 // our value. If value is negavite stop after 2 F's
3432 buffy[--position] = table[(value & mask)];
3433 value = (sbyte)(value >> 4);
3434 } while (value != 0 && position > (size - 2));
3436 // pad w/ 0's if they want more length, if not, ignore
3437 precision -= (size - position);
3438 while (precision > 0 && position > 1) {
3439 buffy[--position] = '0';
3443 return new string(buffy, position, (size - position));
3446 private static string FormatHexadecimal (ushort value, int precision, bool upper)
3448 if (precision < 0) precision = 0;
3449 int size = maxShortLength + precision;
3450 char[] buffy = new char[size];
3451 char[] table = upper ? digitUpperTable : digitLowerTable;
3452 int position = size;
3453 int mask = (1 << 4) - 1;
3455 // loop through right to left, shifting and looking up
3456 // our value. Don't worry about negative
3458 buffy[--position] = table[(value & mask)];
3459 value = (ushort)(value >> 4);
3460 } while (value != 0);
3462 // pad w/ 0's if they want more length, if not, ignore
3463 precision -= (size - position);
3464 while (precision > 0 && position > 1) {
3465 buffy[--position] = '0';
3469 return new string(buffy, position, (size - position));
3472 private static string FormatHexadecimal (uint value, int precision, bool upper)
3474 if (precision < 0) precision = 0;
3475 int size = maxIntLength + precision;
3476 char[] buffy = new char[size];
3477 char[] table = upper ? digitUpperTable : digitLowerTable;
3478 int position = size;
3479 uint mask = (1 << 4) - 1;
3481 // loop through right to left, shifting and looking up
3482 // our value. Don't worry about negative
3484 buffy[--position] = table[(value & mask)];
3486 } while (value != 0);
3488 // pad w/ 0's if they want more length, if not, ignore
3489 precision -= (size - position);
3490 while (precision > 0 && position > 1) {
3491 buffy[--position] = '0';
3495 return new string(buffy, position, (size - position));
3498 private static string FormatHexadecimal (ulong value, int precision, bool upper)
3500 if (precision < 0) precision = 0;
3501 int size = maxLongLength + precision;
3502 char[] buffy = new char[size];
3503 char[] table = upper ? digitUpperTable : digitLowerTable;
3504 int position = size;
3505 ulong mask = (1 << 4) - 1;
3507 // loop through right to left, shifting and looking up
3508 // our value. Don't worry about negative
3510 buffy[--position] = table[value & mask];
3512 } while (value != 0);
3514 // pad w/ 0's if they want more length, if not, ignore
3515 precision -= (size - position);
3516 while (precision > 0 && position > 1) {
3517 buffy[--position] = '0';
3521 return new string(buffy, position, (size - position));
3524 // ============ Format Custom ============ //
3526 private static string FormatCustom (string format, sbyte number, NumberFormatInfo nfi)
3528 string strnum = FormatGeneral (number, -1, nfi, true);
3529 FormatParse fp = new FormatParse (format); // FIXME: use nfi!
3530 int sign = (number < 0) ? -1 : (number > 0) ? 1 : 0;
3531 return fp.FormatNumber (strnum, sign);
3534 private static string FormatCustom (string format, short number, NumberFormatInfo nfi)
3536 string strnum = FormatGeneral (number, -1, nfi, true);
3537 FormatParse fp = new FormatParse (format); // FIXME: use nfi!
3538 int sign = (number < 0) ? -1 : (number > 0) ? 1 : 0;
3539 return fp.FormatNumber (strnum, sign);
3542 private static string FormatCustom (string format, int number, NumberFormatInfo nfi)
3544 string strnum = FormatGeneral (number, -1, nfi, true);
3545 FormatParse fp = new FormatParse (format); // FIXME: use nfi!
3546 int sign = (number < 0) ? -1 : (number > 0) ? 1 : 0;
3547 return fp.FormatNumber (strnum, sign);
3550 private static string FormatCustom (string format, long number, NumberFormatInfo nfi)
3552 string strnum = FormatGeneral (number, -1, nfi, true);
3553 FormatParse fp = new FormatParse (format); // FIXME: use nfi!
3554 int sign = (number < 0) ? -1 : (number > 0) ? 1 : 0;
3555 return fp.FormatNumber (strnum, sign);
3558 private static string FormatCustom (string format, byte number, NumberFormatInfo nfi)
3560 string strnum = FormatGeneral (number, -1, nfi, true);
3561 FormatParse fp = new FormatParse (format); // FIXME: use nfi!
3562 return fp.FormatNumber (strnum, (number == 0) ? 0 : 1);
3565 private static string FormatCustom (string format, ushort number, NumberFormatInfo nfi)
3567 string strnum = FormatGeneral (number, -1, nfi, true);
3568 FormatParse fp = new FormatParse (format); // FIXME: use nfi!
3569 return fp.FormatNumber (strnum, (number == 0) ? 0 : 1);
3572 private static string FormatCustom (string format, uint number, NumberFormatInfo nfi)
3574 string strnum = FormatGeneral (number, -1, nfi, true);
3575 FormatParse fp = new FormatParse (format); // FIXME: use nfi!
3576 return fp.FormatNumber (strnum, (number == 0) ? 0 : 1);
3579 private static string FormatCustom (string format, ulong number, NumberFormatInfo nfi)
3581 string strnum = FormatGeneral (number, -1, nfi, true);
3582 FormatParse fp = new FormatParse (format); // FIXME: use nfi!
3583 return fp.FormatNumber (strnum, (number == 0) ? 0 : 1);
3587 class FormatSection {
3589 public int nphPreDot;
3590 public int npercent;
3591 public int ndividers;
3593 public string [] tokens;
3594 public int [] TokenTypes;
3595 public bool HaveDot;
3596 public bool HaveSci;
3597 public bool sciSignAlways = false;
3598 public int sciDigits;
3599 public int numCommas;
3603 const int AS_IS = 0;
3605 const int PH_NUMBER = 2;
3606 const int COMMA = 3;
3607 const int PERCENT = 4;
3608 const int DIVIDERS = 5;
3610 const int ESCAPE_SEQ = 7;
3611 const int SCIENTIFIC = 8;
3612 const int NEW_SECTION = 9;
3613 private FormatSection [] sections = new FormatSection[3];
3614 private int nsections = 0;
3615 private int pos; // Position in the format string
3616 private int group = 0; // Used in FormatPlain to insert a comma between groups of digits
3617 private bool isNegative;
3619 private FormatParse ()
3623 public FormatParse (string format)
3625 parseFormat (format);
3628 private void FormatSci (char [] digits, ArrayList outputList, FormatSection sec)
3630 int tokidx = sec.ntokens - 1;
3632 // Output everything until we get to the SCIENTIFIC
3633 while (tokidx >= 0 && sec.TokenTypes [tokidx] != SCIENTIFIC){
3634 outputList.Add ((string) sec.tokens [tokidx--]);
3638 int exponent = digits.Length - sec.nph;
3639 outputList.Add ((string) exponent.ToString ());
3640 if (sec.sciSignAlways && exponent > 0)
3641 outputList.Add ("+");
3642 outputList.Add ((string) sec.tokens [tokidx--]);
3646 exponent = -exponent;
3647 newDigits = new char [digits.Length + exponent];
3648 Array.Copy (digits, 0, newDigits, exponent, digits.Length);
3649 for (int i = 0; i < exponent; i++)
3654 // Now format the rest
3657 FormatDot (digits, ref digitIdx, outputList, sec, tokidx, 0);
3659 FormatPlain (digits, ref digitIdx, outputList, sec, tokidx, 0, sec.numCommas > 0);
3662 private void FormatDot (char [] digits, ref int digitIdx, ArrayList outputList,
3663 FormatSection sec, int lastToken, int firstToken)
3665 int tokidx = lastToken;
3668 while (tokidx >= firstToken) {
3669 type = sec.TokenTypes [tokidx];
3670 if (type == DOT || type == PH_NUMBER || type == PH_0)
3676 char [] postDotDigits = new char [sec.nph - sec.nphPreDot];
3677 int max = (postDotDigits.Length > digits.Length) ? digits.Length : postDotDigits.Length;
3678 Array.Copy (digits, 0, postDotDigits, 0, max);
3679 int postDotDigitsIdx = 0;
3680 FormatPlain (postDotDigits, ref postDotDigitsIdx, outputList, sec, lastToken, tokidx, false);
3683 FormatPlain (digits, ref digitIdx, outputList, sec, tokidx, 0, sec.numCommas > 0);
3687 private void FormatPlain (char [] digits, ref int digitIdx, ArrayList outputList,
3688 FormatSection sec, int lastToken, int firstToken, bool insertComma)
3690 int tokidx = lastToken;
3693 while (tokidx >= firstToken) {
3694 type = sec.TokenTypes [tokidx];
3695 if (type == PH_0 || type == PH_NUMBER) {
3696 //FIXME: PH_NUMBER should also check for significant digits
3697 // Console.WriteLine ("group : {0}", group);
3698 int i = sec.tokens [tokidx].Length - 1;
3700 if (insertComma && group == 3) {
3701 outputList.Add (","); // FIXME: from NumberFormatInfo
3705 if (digitIdx < digits.Length)
3706 outputList.Add ((string) digits[digitIdx++].ToString ());
3708 outputList.Add ("0");
3713 while (sec.nph == 0 && digitIdx < digits.Length) {
3714 // Flush the numbers left
3715 if (insertComma && group == 3){
3716 outputList.Add (","); // FIXME: from NumberFormatInfo
3719 outputList.Add ((string) digits [digitIdx++].ToString ());
3724 if (sec.nph == 0 && isNegative)
3725 outputList.Add ("-");
3728 outputList.Add ((string) sec.tokens [tokidx]);
3735 private char [] AdjustDigits (string number, FormatSection sec)
3737 char [] digits = number.ToCharArray ();
3738 char [] newDigits = digits;
3739 int decPointIdx = 0;
3742 decPointIdx -= sec.ndividers * 3;
3743 decPointIdx += sec.npercent * 2;
3745 postDot = sec.nph - sec.nphPreDot;
3746 decPointIdx += postDot;
3749 if (decPointIdx > 0) {
3750 newDigits = new char [digits.Length + decPointIdx];
3751 Array.Copy (digits, 0, newDigits, 0, digits.Length);
3752 for (int i = 0; i < decPointIdx; i++)
3753 newDigits[digits.Length + i] = '0';
3754 } else if (decPointIdx < 0) {
3755 decPointIdx = -decPointIdx;
3756 if (decPointIdx >= digits.Length) {
3759 // The numbers turns into 0 when formatting applied
3760 digits = new char [1] {'0'};
3763 int newLength = digits.Length - decPointIdx + postDot - 1;
3764 newDigits = new char [newLength];
3765 int max = digits.Length >= newLength ? newLength : digits.Length;
3766 Array.Copy (digits, 0, newDigits, 0, max);
3767 if (newLength > digits.Length)
3768 for (int i = 0; i < decPointIdx; i++)
3769 newDigits[digits.Length + i] = '0';
3776 public string FormatNumber (string number, int signValue)
3780 isNegative = signValue < 0;
3782 if (signValue < 0 && nsections > 0)
3784 if (signValue == 0 && nsections > 1)
3787 if (number [0] == '-')
3788 number = number.Substring (1);
3790 FormatSection sec = sections [section];
3791 digits = AdjustDigits (number.ToString (), sec);
3792 if (digits.Length == 1 && digits [0] == '0')
3794 sec = sections [2]; // Format as a 0
3796 sec = sections [0]; // Format as positive
3798 ArrayList outputList = new ArrayList ();
3801 Array.Reverse (digits);
3804 FormatSci (digits, outputList, sec);
3805 else if (sec.HaveDot)
3806 FormatDot (digits, ref digitIdx, outputList, sec, sec.ntokens - 1, 0);
3808 FormatPlain (digits, ref digitIdx, outputList, sec, sec.ntokens - 1, 0, sec.numCommas > 0);
3811 for (int i = outputList.Count - 1; i >= 0; i--) {
3812 result += (string) outputList[i];
3818 private void parseFormat (string format)
3820 char [] fmt_chars = format.ToCharArray ();
3821 int fmtlen = fmt_chars.Length;
3823 int prevType = AS_IS;
3826 sections[0] = new FormatSection();
3827 while (pos < fmtlen) {
3829 token = getNextToken (fmt_chars, fmtlen, out type);
3830 if (type == NEW_SECTION) {
3834 sections[nsections] = new FormatSection();
3836 prevType = AddToken (token, type, prevType);
3841 private int AddToken (string token, int type, int prevType)
3843 FormatSection sec = sections[nsections];
3844 string [] newTokens = new string [sec.ntokens + 1];
3845 int [] newTokenTypes = new int [sec.ntokens + 1];
3846 for (int i = 0; i < sec.ntokens; i++) {
3847 newTokens[i] = sec.tokens[i];
3848 newTokenTypes[i] = sec.TokenTypes[i];
3856 if (!sec.HaveDot && (prevType == PH_0 || prevType == PH_NUMBER)) {
3864 if (!sec.HaveDot && (prevType == PH_0 || prevType == PH_NUMBER ||
3865 prevType == DIVIDERS || prevType == COMMA)) {
3867 sec.nphPreDot = sec.nph;
3878 sec.ndividers = token.Length;
3884 sec.nph += token.Length;
3890 sec.nph += token.Length;
3895 if (!sec.HaveSci && sec.nph > 0) {
3897 char [] sci = token.ToCharArray ();
3898 sec.sciSignAlways = sci[1] == '+' ? true : false;
3899 int expLen = sci[1] == '0' ? token.Length - 1 : token.Length - 2;
3900 sec.sciDigits = expLen;
3901 token = sci[0].ToString ();
3908 newTokens[sec.ntokens] = token;
3909 newTokenTypes[sec.ntokens] = type;
3910 sec.tokens = newTokens;
3911 sec.TokenTypes = newTokenTypes;
3916 private string getNextToken (char [] fmt_chars, int fmtlen, out int type)
3919 string result = null;
3922 type = AS_IS; // Default
3923 current = fmt_chars[curpos];
3924 if (current == ';'){
3926 result = "NEW_SECTION";
3929 else if (current == '\'' || current == '"') {
3930 char Quote = current;
3932 int endpos = Array.IndexOf (fmt_chars, current, curpos);
3935 result = new string (fmt_chars, curpos, endpos - curpos);
3938 else if (current == '\\') { //MS seems not to translate escape seqs!
3940 current = fmt_chars[++pos];
3941 result = current.ToString ();
3944 else if (current == '%') {
3949 else if (current == '.') {
3954 else if (current == ',') {
3955 int begpos = curpos;
3957 while (++curpos < fmtlen && fmt_chars[curpos] == ',');
3958 if (curpos == fmtlen || fmt_chars[curpos] == '.') {
3960 result = new string (fmt_chars, begpos, curpos - begpos);
3969 else if (current == '0' || current == '#') {
3970 char placeHolder = current;
3971 int begpos = curpos;
3972 type = placeHolder == '0' ? PH_0 : PH_NUMBER;
3974 while (curpos < fmtlen && fmt_chars [curpos] == placeHolder)
3976 result = new string (fmt_chars, begpos, curpos - begpos);
3979 else if (current == 'e' || current == 'E') {
3980 if (fmtlen <= curpos + 1){
3981 result = current.ToString ();
3985 char next1 = fmt_chars [curpos + 1];
3987 if (next1 != '-' && next1 != '+' && next1 != '0') {
3988 result = new string (fmt_chars, curpos, 2);
3992 int begpos = curpos;
3994 if (next1 == '-' || next1 == '+')
3999 if (curpos < fmtlen && fmt_chars [curpos] == '0'){
4001 while (curpos < fmtlen && fmt_chars [curpos] == '0')
4005 result = new string (fmt_chars, begpos, curpos - begpos);
4011 char [] format_spec = { '0', '#', ',', '.', '%', 'E', 'e', '"', '\'', '\\' };
4014 while (curpos < fmtlen) {
4015 current = fmt_chars[curpos];
4016 nextFE = Array.IndexOf (format_spec, current);
4022 result = new string (fmt_chars, pos, curpos - pos);