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 // For every integral type.
42 // Before every Format<Format Type> block there is a small paragraph
43 // detailing its requirements, and a blurb of what I was thinking
46 // Some speedup suggestions to be done when after this appears
47 // to be working properly:
49 // * Deal w/ out of range numbers better. Specifically with
50 // regards to boundry cases such as Long.MinValue etc.
51 // The previous way of if (value < 0) value = -value;
52 // fails under this assumption, since the largest
53 // possible MaxValue is < absolute value of the MinValue.
54 // I do the first iteration outside of the loop, and then
55 // convert the number to positive, then continue in the loop.
57 // * Replace all occurances of max<Type>Length with their
58 // numerical values. Plus the places where things are set
59 // to max<Type>Length - 1. Hardcode these to numbers.
61 // * Move the code for all the NumberToString()'s into the
62 // the main ToString (string, NumberFormatInfo) method in
63 // the data types themselves. That way they'd be throwing
64 // their own exceptions on error and it'd save a function
67 // * For integer to char buffer transformation, you could
68 // implement the calculations of the 10's and 100's place
69 // the same time w/ another table to shorten loop time.
71 // * Someone smarter can prolly find a much more efficient
72 // way of formatting the exponential notation. It's still
73 // done in pass, just may have too many repositioning
76 // * Decide whether it be better to have functions that
77 // handle formatting for all types, or just cast their
78 // values out and format them. Just if library size is
79 // more important than speed in saving a cast and a
83 using System.Globalization;
87 internal sealed class IntegerFormatter {
89 private static int maxByteLength = 4;
90 private static int maxShortLength = 6;
91 private static int maxIntLength = 12;
92 private static int maxLongLength = 22;
94 private static char[] digitLowerTable =
95 { '0', '1', '2', '3', '4', '5', '6', '7',
96 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
98 private static char[] digitUpperTable =
99 { '0', '1', '2', '3', '4', '5', '6', '7',
100 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
102 private static bool ParseFormat (string format, out char specifier, out int precision)
107 int length = format.Length;
108 if (length < 1 || length > 3)
111 char[] chars = format.ToCharArray ();
112 specifier = chars[0];
118 if (chars[1] < '0' || chars[1] > '9')
121 precision = chars[1] - '0';
123 if (chars[1] < '0' || chars[2] < '0' || chars[1] > '9' || chars[2] > '9')
126 precision = (chars[1] - '0') * 10 + (chars[2] - '0');
132 // ============ Public Interface to all the integer types ============ //
134 public static string NumberToString (string format, NumberFormatInfo nfi, byte value)
139 if (!ParseFormat (format, out specifier, out precision))
140 throw new FormatException ("The specified format is invalid");
143 case 'c': return FormatCurrency (value, precision, nfi);
144 case 'C': return FormatCurrency (value, precision, nfi);
145 case 'd': return FormatDecimal (value, precision);
146 case 'D': return FormatDecimal (value, precision);
147 case 'e': return FormatExponential (value, precision, false);
148 case 'E': return FormatExponential (value, precision, true);
149 case 'f': return FormatFixedPoint (value, precision, nfi);
150 case 'F': return FormatFixedPoint (value, precision, nfi);
151 case 'g': return FormatGeneral (value, precision, nfi, false);
152 case 'G': return FormatGeneral (value, precision, nfi, true);
153 case 'n': return FormatNumber (value, precision, nfi);
154 case 'N': return FormatNumber (value, precision, nfi);
155 case 'p': return FormatPercent (value, precision, nfi);
156 case 'P': return FormatPercent (value, precision, nfi);
157 case 'r': throw new FormatException ("The specified format cannot be used in this instance");
158 case 'R': throw new FormatException ("The specified format cannot be used in this instance");
159 case 'x': return FormatHexadecimal (value, precision, false);
160 case 'X': return FormatHexadecimal (value, precision, true);
162 throw new FormatException ("The specified format is invalid");
166 public static string NumberToString (string format, NumberFormatInfo nfi, short value)
171 if (!ParseFormat (format, out specifier, out precision))
172 throw new FormatException ("The specified format is invalid");
175 case 'c': return FormatCurrency (value, precision, nfi);
176 case 'C': return FormatCurrency (value, precision, nfi);
177 case 'd': return FormatDecimal (value, precision);
178 case 'D': return FormatDecimal (value, precision);
179 case 'e': return FormatExponential (value, precision, false);
180 case 'E': return FormatExponential (value, precision, true);
181 case 'f': return FormatFixedPoint (value, precision, nfi);
182 case 'F': return FormatFixedPoint (value, precision, nfi);
183 case 'g': return FormatGeneral (value, precision, nfi, false);
184 case 'G': return FormatGeneral (value, precision, nfi, true);
185 case 'n': return FormatNumber (value, precision, nfi);
186 case 'N': return FormatNumber (value, precision, nfi);
187 case 'p': return FormatPercent (value, precision, nfi);
188 case 'P': return FormatPercent (value, precision, nfi);
189 case 'r': throw new FormatException ("The specified format cannot be used in this instance");
190 case 'R': throw new FormatException ("The specified format cannot be used in this instance");
191 case 'x': return FormatHexadecimal (value, precision, false);
192 case 'X': return FormatHexadecimal (value, precision, true);
194 throw new FormatException ("The specified format is invalid");
198 public static string NumberToString (string format, NumberFormatInfo nfi, int value)
203 if (!ParseFormat (format, out specifier, out precision))
204 throw new FormatException ("The specified format is invalid");
207 case 'c': return FormatCurrency (value, precision, nfi);
208 case 'C': return FormatCurrency (value, precision, nfi);
209 case 'd': return FormatDecimal (value, precision);
210 case 'D': return FormatDecimal (value, precision);
211 case 'e': return FormatExponential (value, precision, false);
212 case 'E': return FormatExponential (value, precision, true);
213 case 'f': return FormatFixedPoint (value, precision, nfi);
214 case 'F': return FormatFixedPoint (value, precision, nfi);
215 case 'g': return FormatGeneral (value, precision, nfi, false);
216 case 'G': return FormatGeneral (value, precision, nfi, true);
217 case 'n': return FormatNumber (value, precision, nfi);
218 case 'N': return FormatNumber (value, precision, nfi);
219 case 'p': return FormatPercent (value, precision, nfi);
220 case 'P': return FormatPercent (value, precision, nfi);
221 case 'r': throw new FormatException ("The specified format cannot be used in this instance");
222 case 'R': throw new FormatException ("The specified format cannot be used in this instance");
223 case 'x': return FormatHexadecimal (value, precision, false);
224 case 'X': return FormatHexadecimal (value, precision, true);
226 throw new FormatException ("The specified format is invalid");
230 public static string NumberToString (string format, NumberFormatInfo nfi, long value)
235 if (!ParseFormat (format, out specifier, out precision))
236 throw new FormatException ("The specified format is invalid");
239 case 'c': return FormatCurrency (value, precision, nfi);
240 case 'C': return FormatCurrency (value, precision, nfi);
241 case 'd': return FormatDecimal (value, precision);
242 case 'D': return FormatDecimal (value, precision);
243 case 'e': return FormatExponential (value, precision, false);
244 case 'E': return FormatExponential (value, precision, true);
245 case 'f': return FormatFixedPoint (value, precision, nfi);
246 case 'F': return FormatFixedPoint (value, precision, nfi);
247 case 'g': return FormatGeneral (value, precision, nfi, false);
248 case 'G': return FormatGeneral (value, precision, nfi, true);
249 case 'n': return FormatNumber (value, precision, nfi);
250 case 'N': return FormatNumber (value, precision, nfi);
251 case 'p': return FormatPercent (value, precision, nfi);
252 case 'P': return FormatPercent (value, precision, nfi);
253 case 'r': throw new FormatException ("The specified format cannot be used in this instance");
254 case 'R': throw new FormatException ("The specified format cannot be used in this instance");
255 case 'x': return FormatHexadecimal (value, precision, false);
256 case 'X': return FormatHexadecimal (value, precision, true);
258 throw new FormatException ("The specified format is invalid");
262 public static string NumberToString (string format, NumberFormatInfo nfi, sbyte value)
267 if (!ParseFormat (format, out specifier, out precision))
268 throw new FormatException ("The specified format is invalid");
271 case 'c': return FormatCurrency (value, precision, nfi);
272 case 'C': return FormatCurrency (value, precision, nfi);
273 case 'd': return FormatDecimal (value, precision);
274 case 'D': return FormatDecimal (value, precision);
275 case 'e': return FormatExponential (value, precision, false);
276 case 'E': return FormatExponential (value, precision, true);
277 case 'f': return FormatFixedPoint (value, precision, nfi);
278 case 'F': return FormatFixedPoint (value, precision, nfi);
279 case 'g': return FormatGeneral (value, precision, nfi, false);
280 case 'G': return FormatGeneral (value, precision, nfi, true);
281 case 'n': return FormatNumber (value, precision, nfi);
282 case 'N': return FormatNumber (value, precision, nfi);
283 case 'p': return FormatPercent (value, precision, nfi);
284 case 'P': return FormatPercent (value, precision, nfi);
285 case 'r': throw new FormatException ("The specified format cannot be used in this instance");
286 case 'R': throw new FormatException ("The specified format cannot be used in this instance");
287 case 'x': return FormatHexadecimal (value, precision, false);
288 case 'X': return FormatHexadecimal (value, precision, true);
290 throw new FormatException ("The specified format is invalid");
294 public static string NumberToString (string format, NumberFormatInfo nfi, ushort value)
299 if (!ParseFormat (format, out specifier, out precision))
300 throw new FormatException ("The specified format is invalid");
303 case 'c': return FormatCurrency (value, precision, nfi);
304 case 'C': return FormatCurrency (value, precision, nfi);
305 case 'd': return FormatDecimal (value, precision);
306 case 'D': return FormatDecimal (value, precision);
307 case 'e': return FormatExponential (value, precision, false);
308 case 'E': return FormatExponential (value, precision, true);
309 case 'f': return FormatFixedPoint (value, precision, nfi);
310 case 'F': return FormatFixedPoint (value, precision, nfi);
311 case 'g': return FormatGeneral (value, precision, nfi, false);
312 case 'G': return FormatGeneral (value, precision, nfi, true);
313 case 'n': return FormatNumber (value, precision, nfi);
314 case 'N': return FormatNumber (value, precision, nfi);
315 case 'p': return FormatPercent (value, precision, nfi);
316 case 'P': return FormatPercent (value, precision, nfi);
317 case 'r': throw new FormatException ("The specified format cannot be used in this instance");
318 case 'R': throw new FormatException ("The specified format cannot be used in this instance");
319 case 'x': return FormatHexadecimal (value, precision, false);
320 case 'X': return FormatHexadecimal (value, precision, true);
322 throw new FormatException ("The specified format is invalid");
326 public static string NumberToString (string format, NumberFormatInfo nfi, uint value)
331 if (!ParseFormat (format, out specifier, out precision))
332 throw new FormatException ("The specified format is invalid");
335 case 'c': return FormatCurrency (value, precision, nfi);
336 case 'C': return FormatCurrency (value, precision, nfi);
337 case 'd': return FormatDecimal (value, precision);
338 case 'D': return FormatDecimal (value, precision);
339 case 'e': return FormatExponential (value, precision, false);
340 case 'E': return FormatExponential (value, precision, true);
341 case 'f': return FormatFixedPoint (value, precision, nfi);
342 case 'F': return FormatFixedPoint (value, precision, nfi);
343 case 'g': return FormatGeneral (value, precision, nfi, false);
344 case 'G': return FormatGeneral (value, precision, nfi, true);
345 case 'n': return FormatNumber (value, precision, nfi);
346 case 'N': return FormatNumber (value, precision, nfi);
347 case 'p': return FormatPercent (value, precision, nfi);
348 case 'P': return FormatPercent (value, precision, nfi);
349 case 'r': throw new FormatException ("The specified format cannot be used in this instance");
350 case 'R': throw new FormatException ("The specified format cannot be used in this instance");
351 case 'x': return FormatHexadecimal (value, precision, false);
352 case 'X': return FormatHexadecimal (value, precision, true);
354 throw new FormatException ("The specified format is invalid");
358 public static string NumberToString (string format, NumberFormatInfo nfi, ulong value)
363 if (!ParseFormat (format, out specifier, out precision))
364 throw new FormatException ("The specified format is invalid");
367 case 'c': return FormatCurrency (value, precision, nfi);
368 case 'C': return FormatCurrency (value, precision, nfi);
369 case 'd': return FormatDecimal (value, precision);
370 case 'D': return FormatDecimal (value, precision);
371 case 'e': return FormatExponential (value, precision, false);
372 case 'E': return FormatExponential (value, precision, true);
373 case 'f': return FormatFixedPoint (value, precision, nfi);
374 case 'F': return FormatFixedPoint (value, precision, nfi);
375 case 'g': return FormatGeneral (value, precision, nfi, false);
376 case 'G': return FormatGeneral (value, precision, nfi, true);
377 case 'n': return FormatNumber (value, precision, nfi);
378 case 'N': return FormatNumber (value, precision, nfi);
379 case 'p': return FormatPercent (value, precision, nfi);
380 case 'P': return FormatPercent (value, precision, nfi);
381 case 'r': throw new FormatException ("The specified format cannot be used in this instance");
382 case 'R': throw new FormatException ("The specified format cannot be used in this instance");
383 case 'x': return FormatHexadecimal (value, precision, false);
384 case 'X': return FormatHexadecimal (value, precision, true);
386 throw new FormatException ("The specified format is invalid");
390 // ============ Currency Type Formating ============ //
393 // Currency Format: Used for strings containing a monetary value. The
394 // CurrencySymbol, CurrencyGroupSizes, CurrencyGroupSeparator, and
395 // CurrencyDecimalSeparator members of a NumberFormatInfo supply
396 // the currency symbol, size and separator for digit groupings, and
397 // decimal separator, respectively.
398 // CurrencyNegativePattern and CurrencyPositivePattern determine the
399 // symbols used to represent negative and positive values. For example,
400 // a negative value may be prefixed with a minus sign, or enclosed in
402 // If the precision specifier is omitted
403 // NumberFormatInfo.CurrencyDecimalDigits determines the number of
404 // decimal places in the string. Results are rounded to the nearest
405 // representable value when necessary.
407 // The pattern of the NumberFormatInfo determines how the output looks, where
408 // the dollar sign goes, where the negative sign goes, etc.
409 // IFormattable documentation lists the patterns and their values,
410 // I have them commented out in the large switch statement
413 private static string FormatCurrency (byte value, int precision, NumberFormatInfo nfi)
415 return FormatCurrency ((uint)value, precision, nfi);
418 private static string FormatCurrency (short value, int precision, NumberFormatInfo nfi)
420 return FormatCurrency ((int)value, precision, nfi);
423 private static string FormatCurrency (int value, int precision, NumberFormatInfo nfi)
426 bool negative = (value < 0);
428 char[] groupSeparator = nfi.CurrencyGroupSeparator.ToCharArray ();
429 char[] decimalSeparator = nfi.CurrencyDecimalSeparator.ToCharArray ();
430 char[] currencySymbol = nfi.CurrencySymbol.ToCharArray ();
431 int[] groupSizes = nfi.CurrencyGroupSizes;
432 int pattern = negative ? nfi.CurrencyNegativePattern : nfi.CurrencyPositivePattern;
433 int symbolLength = currencySymbol.Length;
435 int padding = (precision >= 0) ? precision : nfi.CurrencyDecimalDigits;
436 int size = maxIntLength + (groupSeparator.Length * maxIntLength) + padding + 2 +
437 decimalSeparator.Length + symbolLength;
438 char[] buffy = new char[size];
441 // set up the pattern from IFormattible
447 buffy[--position] = ')';
454 buffy[--position] = '-';
457 buffy[--position] = ')';
459 buffy[--position] = currencySymbol[--i];
464 buffy[--position] = currencySymbol[--i];
469 buffy[--position] = currencySymbol[--i];
471 buffy[--position] = '-';
474 buffy[--position] = '-';
476 buffy[--position] = currencySymbol[--i];
481 buffy[--position] = currencySymbol[--i];
483 buffy[--position] = ' ';
488 buffy[--position] = '-';
490 buffy[--position] = currencySymbol[--i];
492 buffy[--position] = ' ';
495 buffy[--position] = '-';
497 // case 12: // $ -nnn
501 buffy[--position] = currencySymbol[--i];
503 buffy[--position] = ' ';
504 buffy[--position] = '-';
507 buffy[--position] = ')';
510 buffy[--position] = ')';
512 buffy[--position] = currencySymbol[--i];
523 buffy[--position] = currencySymbol[--i];
530 buffy[--position] = currencySymbol[--i];
532 buffy[--position] = ' ';
537 // right pad it w/ precision 0's
538 while (padding-- > 0)
539 buffy[--position] = '0';
541 // put on decimal separator if we moved over and put a 0
542 if (position < size && buffy[position] == '0') {
543 i = decimalSeparator.Length;
545 buffy[--position] = decimalSeparator[--i];
549 // loop through, keeping track of where you are in the
550 // group sizes array and putting out the group separator
555 // just in place to take care of the negative boundries (Int32.MinValue)
558 buffy[--position] = digitLowerTable[-(value % 10)];
562 i = groupSeparator.Length;
564 buffy[--position] = groupSeparator[--i];
567 k = (j < groupSizes.Length) ?
568 groupSizes[j++] : groupSizes[(groupSizes.Length - 1)];
570 } else value = -value;
573 while (value >= 10) {
574 buffy[--position] = digitLowerTable[(value % 10)];
578 i = groupSeparator.Length;
580 buffy[--position] = groupSeparator[--i];
583 k = (j < groupSizes.Length) ?
584 groupSizes[j++] : groupSizes[(groupSizes.Length - 1)];
588 buffy[--position] = digitLowerTable[value];
590 // end the pattern on the left hand side
597 buffy[--position] = currencySymbol[--i];
599 buffy[--position] = '(';
603 buffy[--position] = currencySymbol[--i];
605 buffy[--position] = '-';
608 buffy[--position] = '-';
610 buffy[--position] = currencySymbol[--i];
615 buffy[--position] = currencySymbol[--i];
619 buffy[--position] = '(';
622 buffy[--position] = '-';
629 buffy[--position] = '-';
633 // case 10: // nnn $-
636 buffy[--position] = ' ';
638 buffy[--position] = currencySymbol[--i];
642 buffy[--position] = '-';
643 buffy[--position] = ' ';
645 buffy[--position] = currencySymbol[--i];
648 // case 13: // nnn- $
651 buffy[--position] = ' ';
653 buffy[--position] = currencySymbol[--i];
655 buffy[--position] = '(';
658 buffy[--position] = '(';
666 buffy[--position] = currencySymbol[--i];
672 buffy[--position] = ' ';
674 buffy[--position] = currencySymbol[--i];
682 return new string (buffy, position, (size - position));
685 private static string FormatCurrency (long value, int precision, NumberFormatInfo nfi)
688 bool negative = (value < 0);
690 char[] groupSeparator = nfi.CurrencyGroupSeparator.ToCharArray ();
691 char[] decimalSeparator = nfi.CurrencyDecimalSeparator.ToCharArray ();
692 char[] currencySymbol = nfi.CurrencySymbol.ToCharArray ();
693 int[] groupSizes = nfi.CurrencyGroupSizes;
694 int pattern = negative ? nfi.CurrencyNegativePattern : nfi.CurrencyPositivePattern;
695 int symbolLength = currencySymbol.Length;
697 int padding = (precision >= 0) ? precision : nfi.CurrencyDecimalDigits;
698 int size = maxLongLength + (groupSeparator.Length * maxLongLength) + padding + 2 +
699 decimalSeparator.Length + symbolLength;
700 char[] buffy = new char[size];
703 // set up the pattern from IFormattible
709 buffy[--position] = ')';
716 buffy[--position] = '-';
719 buffy[--position] = ')';
721 buffy[--position] = currencySymbol[--i];
726 buffy[--position] = currencySymbol[--i];
731 buffy[--position] = currencySymbol[--i];
733 buffy[--position] = '-';
736 buffy[--position] = '-';
738 buffy[--position] = currencySymbol[--i];
743 buffy[--position] = currencySymbol[--i];
745 buffy[--position] = ' ';
750 buffy[--position] = '-';
752 buffy[--position] = currencySymbol[--i];
754 buffy[--position] = ' ';
757 buffy[--position] = '-';
759 // case 12: // $ -nnn
763 buffy[--position] = currencySymbol[--i];
765 buffy[--position] = ' ';
766 buffy[--position] = '-';
769 buffy[--position] = ')';
772 buffy[--position] = ')';
774 buffy[--position] = currencySymbol[--i];
785 buffy[--position] = currencySymbol[--i];
792 buffy[--position] = currencySymbol[--i];
794 buffy[--position] = ' ';
799 // right pad it w/ precision 0's
800 while (padding-- > 0)
801 buffy[--position] = '0';
803 // put on decimal separator if we moved over and put a 0
804 if (position < size && buffy[position] == '0') {
805 i = decimalSeparator.Length;
807 buffy[--position] = decimalSeparator[--i];
811 // loop through, keeping track of where you are in the
812 // group sizes array and putting out the group separator
819 buffy[--position] = digitLowerTable[-(value % 10)];
823 i = groupSeparator.Length;
825 buffy[--position] = groupSeparator[--i];
828 k = (j < groupSizes.Length) ?
829 groupSizes[j++] : groupSizes[(groupSizes.Length - 1)];
831 } else value = -value;
834 while (value >= 10) {
835 buffy[--position] = digitLowerTable[(value % 10)];
839 i = groupSeparator.Length;
841 buffy[--position] = groupSeparator[--i];
844 k = (j < groupSizes.Length) ?
845 groupSizes[j++] : groupSizes[(groupSizes.Length - 1)];
849 buffy[--position] = digitLowerTable[value];
851 // end the pattern on the left hand side
858 buffy[--position] = currencySymbol[--i];
860 buffy[--position] = '(';
864 buffy[--position] = currencySymbol[--i];
866 buffy[--position] = '-';
869 buffy[--position] = '-';
871 buffy[--position] = currencySymbol[--i];
876 buffy[--position] = currencySymbol[--i];
880 buffy[--position] = '(';
883 buffy[--position] = '-';
890 buffy[--position] = '-';
894 // case 10: // nnn $-
897 buffy[--position] = ' ';
899 buffy[--position] = currencySymbol[--i];
903 buffy[--position] = '-';
904 buffy[--position] = ' ';
906 buffy[--position] = currencySymbol[--i];
909 // case 13: // nnn- $
912 buffy[--position] = ' ';
914 buffy[--position] = currencySymbol[--i];
916 buffy[--position] = '(';
919 buffy[--position] = '(';
927 buffy[--position] = currencySymbol[--i];
933 buffy[--position] = ' ';
935 buffy[--position] = currencySymbol[--i];
943 return new string (buffy, position, (size - position));
946 private static string FormatCurrency (sbyte value, int precision, NumberFormatInfo nfi)
948 return FormatCurrency ((int)value, precision, nfi);
951 private static string FormatCurrency (ushort value, int precision, NumberFormatInfo nfi)
953 return FormatCurrency ((uint)value, precision, nfi);
956 private static string FormatCurrency (uint value, int precision, NumberFormatInfo nfi)
960 char[] groupSeparator = nfi.CurrencyGroupSeparator.ToCharArray ();
961 char[] decimalSeparator = nfi.CurrencyDecimalSeparator.ToCharArray ();
962 char[] currencySymbol = nfi.CurrencySymbol.ToCharArray ();
963 int[] groupSizes = nfi.CurrencyGroupSizes;
964 int pattern = nfi.CurrencyPositivePattern;
965 int symbolLength = currencySymbol.Length;
967 int padding = (precision >= 0) ? precision : nfi.CurrencyDecimalDigits;
968 int size = maxIntLength + (groupSeparator.Length * maxIntLength) + padding + 2 +
969 decimalSeparator.Length + symbolLength;
970 char[] buffy = new char[size];
973 // set up the pattern from IFormattible, no negative
980 buffy[--position] = currencySymbol[--i];
987 buffy[--position] = currencySymbol[--i];
989 buffy[--position] = ' ';
993 // right pad it w/ precision 0's
994 while (padding-- > 0)
995 buffy[--position] = '0';
997 // put on decimal separator if we moved over and put a 0
998 if (position < size && buffy[position] == '0') {
999 i = decimalSeparator.Length;
1001 buffy[--position] = decimalSeparator[--i];
1005 // loop through, keeping track of where you are in the
1006 // group sizes array and putting out the group separator
1009 k = groupSizes[j++];
1011 while (value >= 10) {
1012 buffy[--position] = digitLowerTable[(value % 10)];
1016 i = groupSeparator.Length;
1018 buffy[--position] = groupSeparator[--i];
1021 k = (j < groupSizes.Length) ?
1022 groupSizes[j++] : groupSizes[(groupSizes.Length - 1)];
1026 buffy[--position] = digitLowerTable[value];
1028 // end the pattern on the left hand side
1033 buffy[--position] = currencySymbol[--i];
1039 buffy[--position] = ' ';
1041 buffy[--position] = currencySymbol[--i];
1048 return new string (buffy, position, (size - position));
1051 private static string FormatCurrency (ulong value, int precision, NumberFormatInfo nfi)
1055 char[] groupSeparator = nfi.CurrencyGroupSeparator.ToCharArray ();
1056 char[] decimalSeparator = nfi.CurrencyDecimalSeparator.ToCharArray ();
1057 char[] currencySymbol = nfi.CurrencySymbol.ToCharArray ();
1058 int[] groupSizes = nfi.CurrencyGroupSizes;
1059 int pattern = nfi.CurrencyPositivePattern;
1060 int symbolLength = currencySymbol.Length;
1062 int padding = (precision >= 0) ? precision : nfi.CurrencyDecimalDigits;
1063 int size = maxLongLength + (groupSeparator.Length * maxLongLength) + padding + 2 +
1064 decimalSeparator.Length + symbolLength;
1065 char[] buffy = new char[size];
1066 int position = size;
1068 // set up the pattern from IFormattible, no negative
1075 buffy[--position] = currencySymbol[--i];
1082 buffy[--position] = currencySymbol[--i];
1084 buffy[--position] = ' ';
1088 // right pad it w/ precision 0's
1089 while (padding-- > 0)
1090 buffy[--position] = '0';
1092 // put on decimal separator if we moved over and put a 0
1093 if (position < size && buffy[position] == '0') {
1094 i = decimalSeparator.Length;
1096 buffy[--position] = decimalSeparator[--i];
1100 // loop through, keeping track of where you are in the
1101 // group sizes array and putting out the group separator
1104 k = groupSizes[j++];
1106 while (value >= 10) {
1107 buffy[--position] = digitLowerTable[(value % 10)];
1111 i = groupSeparator.Length;
1113 buffy[--position] = groupSeparator[--i];
1116 k = (j < groupSizes.Length) ?
1117 groupSizes[j++] : groupSizes[(groupSizes.Length - 1)];
1121 buffy[--position] = digitLowerTable[value];
1123 // end the pattern on the left hand side
1128 buffy[--position] = currencySymbol[--i];
1134 buffy[--position] = ' ';
1136 buffy[--position] = currencySymbol[--i];
1143 return new string (buffy, position, (size - position));
1146 // ============ Format Decimal Types ============ //
1149 // Used only for integral data types. Negative values are
1150 // represented by using a '-' sign. The precision specifies
1151 // how many digits are to appear in the string. If it is >
1152 // how many digits we need, the left side is padded w/ 0's.
1153 // If it is smaller than what we need, it is discarded.
1155 // Fairly simple implementation. Fill the buffer from right
1156 // to left w/ numbers, then if we still have precision left
1157 // over, pad w/ zeros.
1160 private static string FormatDecimal (byte value, int precision)
1162 return FormatDecimal ((uint)value, precision);
1165 private static string FormatDecimal (short value, int precision)
1167 return FormatDecimal ((int)value, precision);
1170 private static string FormatDecimal (int value, int precision)
1172 int size = (precision > 0) ? (maxIntLength + precision) : maxIntLength;
1173 char[] buffy = new char[size];
1174 int position = size;
1175 bool negative = (value < 0);
1179 buffy[--position] = digitLowerTable[-(value % 10)];
1180 value = value / -10;
1181 } else value = -value;
1183 // get our value into a buffer from right to left
1184 while (value >= 10) {
1185 buffy[--position] = digitLowerTable[(value % 10)];
1189 buffy[--position] = digitLowerTable[value];
1191 // if we have precision left over, fill with 0's
1192 precision -= (size - position);
1193 while (precision-- > 0 && position > 1)
1194 buffy[--position] = '0';
1197 buffy[--position] = '-';
1199 return new string (buffy, position, (size - position));
1202 private static string FormatDecimal (long value, int precision)
1204 int size = (precision > 0) ? (maxLongLength + precision) : maxLongLength;
1205 char[] buffy = new char[size];
1206 int position = size;
1207 bool negative = (value < 0);
1211 buffy[--position] = digitLowerTable[-(value % 10)];
1212 value = value / -10;
1213 } else value = -value;
1215 // get our value into a buffer from right to left
1216 while (value >= 10) {
1217 buffy[--position] = digitLowerTable[(value % 10)];
1221 buffy[--position] = digitLowerTable[value];
1223 // if we have precision left over, fill with 0's
1224 precision -= (size - position);
1225 while (precision-- > 0 && position > 1)
1226 buffy[--position] = '0';
1229 buffy[--position] = '-';
1231 return new string (buffy, position, (size - position));
1234 private static string FormatDecimal (sbyte value, int precision)
1236 return FormatDecimal ((int)value, precision);
1239 private static string FormatDecimal (ushort value, int precision)
1241 return FormatDecimal ((uint)value, precision);
1244 private static string FormatDecimal (uint value, int precision)
1246 int size = (precision > 0) ? (maxIntLength + precision) : maxIntLength;
1247 char[] buffy = new char[size];
1248 int position = size;
1250 // get our value into a buffer from right to left
1251 while (value >= 10) {
1252 buffy[--position] = digitLowerTable[(value % 10)];
1256 buffy[--position] = digitLowerTable[value];
1258 // if we have precision left over, fill with 0's
1259 precision -= (size - position);
1260 while (precision-- > 0 && position > 1)
1261 buffy[--position] = '0';
1263 return new string (buffy, position, (size - position));
1266 private static string FormatDecimal (ulong value, int precision)
1268 int size = (precision > 0) ? (maxLongLength + precision) : maxLongLength;
1269 char[] buffy = new char[size];
1270 int position = size;
1272 // get our value into a buffer from right to left
1273 while (value >= 10) {
1274 buffy[--position] = digitLowerTable[(value % 10)];
1278 buffy[--position] = digitLowerTable[value];
1280 // if we have precision left over, fill with 0's
1281 precision -= (size - position);
1282 while (precision-- > 0 && position > 1)
1283 buffy[--position] = '0';
1285 return new string (buffy, position, (size - position));
1288 // ============ Format Exponentials ============ //
1291 // Used for strings in the format [-]M.DDDDDDe+XXX.
1292 // Exaclty one non-zero digit must appear in M, w/
1293 // a '-' sign if negative. The precision determines
1294 // number of decimal places, if not given go 6 places.
1295 // If precision > the number of places we need, it
1296 // is right padded w/ 0's. If it is smaller than what
1297 // we need, we cut off and round. The format specifier
1298 // decides whether we use an uppercase E or lowercase e.
1300 // Tried to do this in one pass of one buffer, but it
1301 // wasn't happening. Get a buffer + 7 extra slots for
1302 // the -, ., E, +, and XXX. Parse the value into another
1303 // temp buffer, then build the new string. For the
1304 // integral data types, there are a couple things that
1305 // can be hardcoded. Since an int and a long can't be
1306 // larger than 20 something spaces, the first X w/
1307 // always be 0, and the the exponential value will only
1308 // be 2 digits long. Also integer types w/ always
1309 // have a positive exponential.
1312 private static string FormatExponential (byte value, int precision, bool upper)
1314 return FormatExponential ((uint)value, precision, upper);
1317 private static string FormatExponential (short value, int precision, bool upper)
1319 return FormatExponential ((int)value, precision, upper);
1322 private static string FormatExponential (int value, int precision, bool upper)
1324 bool negative = (value < 0);
1325 int padding = (precision >= 0) ? precision : 6;
1326 char[] buffy = new char[(padding + 8)];
1327 char[] tmp = new char [maxIntLength];
1328 int exponent = 0, position = maxIntLength;
1329 int exp = 0, idx = 0;
1332 // ugly, but doing it since abs(Int32.MinValue) > Int.MaxValue
1333 uint number = (negative) ? (uint)((-(value + 1)) + 1) : (uint)value;
1335 // need to calculate the number of places to know if we need to round later
1336 if (negative && value <= -10) {
1341 while (value >= 10) {
1346 if (exp > padding) {
1348 // highest number we should goto before we round
1349 while (idx++ <= padding)
1352 // get our value into a buffer
1353 while (number > pow) {
1354 tmp[--position] = digitLowerTable[(number % 10)];
1362 while (number >= 10) {
1363 tmp[--position] = digitLowerTable[(number% 10)];
1368 tmp[--position] = digitLowerTable[number];
1371 // go left to right in filling up new string
1375 // we know we have at least one in there, followed
1376 // by a decimal point
1377 buffy[idx++] = tmp[position++];
1381 // copy over the remaining digits until we run out,
1382 // or we've passed our specified precision
1383 while (padding > 0 && position < maxIntLength) {
1384 buffy[idx++] = tmp[position++];
1388 // if we still have more precision to go, add some
1390 while (padding > 0) {
1395 // we know these next 3 spots
1396 buffy[idx++] = upper ? 'E' : 'e';
1400 // next two digits depend on our length
1401 if (exponent >= 10) {
1402 buffy[idx++] = digitLowerTable[(exponent / 10)];
1403 buffy[idx] = digitLowerTable[(exponent % 10)];
1406 buffy[idx] = digitLowerTable[exponent];
1409 return new string(buffy, 0, ++idx);
1412 private static string FormatExponential (long value, int precision, bool upper)
1414 bool negative = (value < 0);
1415 int padding = (precision >= 0) ? precision : 6;
1416 char[] buffy = new char[(padding + 8)];
1417 char[] tmp = new char [maxLongLength];
1418 int exponent = 0, position = maxLongLength;
1419 int exp = 0, idx = 0;
1422 // ugly, but doing it since abs(Int32.MinValue) > Int.MaxValue
1423 ulong number = (negative) ? (ulong)((-(value + 1)) + 1) : (ulong)value;
1425 // need to calculate the number of places to know if we need to round later
1426 if (negative && value <= -10) {
1431 while (value >= 10) {
1436 if (exp > padding) {
1438 // highest number we should goto before we round
1439 while (idx++ <= padding)
1442 // get our value into a buffer
1443 while (number > pow) {
1444 tmp[--position] = digitLowerTable[(number % 10)];
1452 while (number >= 10) {
1453 tmp[--position] = digitLowerTable[(number% 10)];
1458 tmp[--position] = digitLowerTable[number];
1461 // go left to right in filling up new string
1465 // we know we have at least one in there, followed
1466 // by a decimal point
1467 buffy[idx++] = tmp[position++];
1471 // copy over the remaining digits until we run out,
1472 // or we've passed our specified precision
1473 while (padding > 0 && position < maxLongLength) {
1474 buffy[idx++] = tmp[position++];
1478 // if we still have more precision to go, add some
1480 while (padding > 0) {
1485 // we know these next 3 spots
1486 buffy[idx++] = upper ? 'E' : 'e';
1490 // next two digits depend on our length
1491 if (exponent >= 10) {
1492 buffy[idx++] = digitLowerTable[(exponent / 10)];
1493 buffy[idx] = digitLowerTable[(exponent % 10)];
1496 buffy[idx] = digitLowerTable[exponent];
1499 return new string(buffy, 0, ++idx);
1502 private static string FormatExponential (sbyte value, int precision, bool upper)
1504 return FormatExponential ((int)value, precision, upper);
1507 private static string FormatExponential (ushort value, int precision, bool upper)
1509 return FormatExponential ((uint)value, precision, upper);
1512 private static string FormatExponential (uint value, int precision, bool upper)
1514 int padding = (precision >= 0) ? precision : 6;
1515 char[] buffy = new char[(padding + 8)];
1516 char[] tmp = new char [maxIntLength];
1517 int exponent = 0, position = maxIntLength;
1518 int exp = 0, idx = 0;
1520 uint number = value;
1522 // need to calculate the number of places to know if we need to round later
1523 while (value >= 10) {
1528 if (exp > padding) {
1530 // highest number we should goto before we round
1531 while (idx++ <= padding)
1534 // get our value into a buffer
1535 while (number > pow) {
1536 tmp[--position] = digitLowerTable[(number % 10)];
1544 while (number >= 10) {
1545 tmp[--position] = digitLowerTable[(number% 10)];
1550 tmp[--position] = digitLowerTable[number];
1553 // we know we have at least one in there, followed
1554 // by a decimal point
1555 buffy[idx++] = tmp[position++];
1559 // copy over the remaining digits until we run out,
1560 // or we've passed our specified precision
1561 while (padding > 0 && position < maxIntLength) {
1562 buffy[idx++] = tmp[position++];
1566 // if we still have more precision to go, add some
1568 while (padding > 0) {
1573 // we know these next 3 spots
1574 buffy[idx++] = upper ? 'E' : 'e';
1578 // next two digits depend on our length
1579 if (exponent >= 10) {
1580 buffy[idx++] = digitLowerTable[(exponent / 10)];
1581 buffy[idx] = digitLowerTable[(exponent % 10)];
1584 buffy[idx] = digitLowerTable[exponent];
1587 return new string(buffy, 0, ++idx);
1590 private static string FormatExponential (ulong value, int precision, bool upper)
1592 int padding = (precision >= 0) ? precision : 6;
1593 char[] buffy = new char[(padding + 8)];
1594 char[] tmp = new char [maxLongLength];
1595 int exponent = 0, position = maxLongLength;
1596 int exp = 0, idx = 0;
1598 ulong number = value;
1600 // need to calculate the number of places to know if we need to round later
1601 while (value >= 10) {
1606 if (exp > padding) {
1608 // highest number we should goto before we round
1609 while (idx++ <= padding)
1612 // get our value into a buffer
1613 while (number > pow) {
1614 tmp[--position] = digitLowerTable[(number % 10)];
1622 while (number >= 10) {
1623 tmp[--position] = digitLowerTable[(number% 10)];
1628 tmp[--position] = digitLowerTable[number];
1631 // we know we have at least one in there, followed
1632 // by a decimal point
1633 buffy[idx++] = tmp[position++];
1637 // copy over the remaining digits until we run out,
1638 // or we've passed our specified precision
1639 while (padding > 0 && position < maxLongLength) {
1640 buffy[idx++] = tmp[position++];
1644 // if we still have more precision to go, add some
1646 while (padding > 0) {
1651 // we know these next 3 spots
1652 buffy[idx++] = upper ? 'E' : 'e';
1656 // next two digits depend on our length
1657 if (exponent >= 10) {
1658 buffy[idx++] = digitLowerTable[(exponent / 10)];
1659 buffy[idx] = digitLowerTable[(exponent % 10)];
1662 buffy[idx] = digitLowerTable[exponent];
1665 return new string(buffy, 0, ++idx);
1668 // ============ Format Fixed Points ============ //
1671 // Used for strings in the following form "[-]M.DD...D"
1672 // At least one non-zero digit precedes the '.', w/ a
1673 // '-' before that if negative. Precision specifies number
1674 // of decimal places 'D' to go. If not given, use
1675 // NumberFormatInfo.NumbeDecimalDigits. Results are rounded
1678 // Fairly simple implementation for integral types. Going
1679 // from right to left, fill up precision number of 0's,
1680 // plop a . down, then go for our number.
1683 private static string FormatFixedPoint (byte value, int precision, NumberFormatInfo nfi)
1685 return FormatFixedPoint ((uint)value, precision, nfi);
1688 private static string FormatFixedPoint (short value, int precision, NumberFormatInfo nfi)
1690 return FormatFixedPoint ((int)value, precision, nfi);
1693 private static string FormatFixedPoint (int value, int precision, NumberFormatInfo nfi)
1695 int padding = (precision >= 0) ? (precision + maxIntLength) : (nfi.NumberDecimalDigits + maxIntLength);
1696 char[] buffy = new char[padding];
1697 int position = padding;
1698 bool negative = (value < 0);
1700 // fill up w/ precision # of 0's
1701 while (position > (maxIntLength - 1))
1702 buffy[--position] = '0';
1705 buffy[position--] = '.';
1709 buffy[position--] = digitLowerTable[-(value % 10)];
1710 value = value / -10;
1711 } else value = -value;
1713 // fill up w/ the value
1714 while (value >= 10) {
1715 buffy[position--] = digitLowerTable[(value % 10)];
1719 buffy[position] = digitLowerTable[value];
1722 buffy[--position] = '-';
1724 return new string (buffy, position, (padding - position));
1727 private static string FormatFixedPoint (long value, int precision, NumberFormatInfo nfi)
1729 int padding = (precision >= 0) ? (precision + maxLongLength) : (nfi.NumberDecimalDigits + maxLongLength);
1730 char[] buffy = new char[padding];
1731 int position = padding;
1732 bool negative = (value < 0);
1734 // fill up w/ precision # of 0's
1735 while (position > (maxLongLength - 1))
1736 buffy[--position] = '0';
1739 buffy[position--] = '.';
1743 buffy[position--] = digitLowerTable[-(value % 10)];
1744 value = value / -10;
1745 } else value = -value;
1747 // fill up w/ the value
1748 while (value >= 10) {
1749 buffy[position--] = digitLowerTable[(value % 10)];
1753 buffy[position] = digitLowerTable[value];
1756 buffy[--position] = '-';
1758 return new string (buffy, position, (padding - position));
1761 private static string FormatFixedPoint (sbyte value, int precision, NumberFormatInfo nfi)
1763 return FormatFixedPoint ((int)value, precision, nfi);
1766 private static string FormatFixedPoint (ushort value, int precision, NumberFormatInfo nfi)
1768 return FormatFixedPoint ((uint)value, precision, nfi);
1771 private static string FormatFixedPoint (uint value, int precision, NumberFormatInfo nfi)
1773 int padding = (precision >= 0) ? (precision + maxIntLength) : (nfi.NumberDecimalDigits + maxIntLength);
1774 char[] buffy = new char[padding];
1775 int position = padding;
1777 // fill up w/ precision # of 0's
1778 while (position > (maxIntLength - 1))
1779 buffy[--position] = '0';
1782 buffy[position--] = '.';
1784 // fill up w/ the value
1785 while (value >= 10) {
1786 buffy[position--] = digitLowerTable[(value % 10)];
1790 buffy[position] = digitLowerTable[value];
1792 return new string (buffy, position, (padding - position));
1795 private static string FormatFixedPoint (ulong value, int precision, NumberFormatInfo nfi)
1797 int padding = (precision >= 0) ? (precision + maxLongLength) : (nfi.NumberDecimalDigits + maxLongLength);
1798 char[] buffy = new char[padding];
1799 int position = padding;
1801 // fill up w/ precision # of 0's
1802 while (position > (maxLongLength - 1))
1803 buffy[--position] = '0';
1806 buffy[position--] = '.';
1808 // fill up w/ the value
1809 while (value >= 10) {
1810 buffy[position--] = digitLowerTable[(value % 10)];
1814 buffy[position] = digitLowerTable[value];
1816 return new string (buffy, position, (padding - position));
1819 // ============ Format General ============ //
1822 // Strings are formatted in either Fixed Point or Exponential
1823 // format. Results are rounded when needed. If no precision is
1824 // given, the defaults are:
1826 // short & ushort: 5
1833 // The value is formatted using fixed-point if exponent >= -4
1834 // and exponent < precision, where exponent is he exponenent of
1835 // the value in exponential format. The decimal point and trailing
1836 // zeros are removed when possible.
1838 // For all other values, exponential format is used. The case of
1839 // the format specifier determines whether 'e' or 'E' prefixes
1842 // In either case, the number of digits that appear in the result
1843 // (not including the exponent) will not exceed the value of the
1844 // precision. The result is rounded as needed.
1846 // Integral values are formatted using Fixed Point whenever
1847 // precision is omitted. (This actually doesn't make sense when
1848 // coupled w/ the 1st paragraph).
1850 // Okay, so the decimal point is removed along with any trailing
1851 // zeros. So, ignoring the last paragraph, we can consider an int
1852 // ToString() to format it w/ exponential format w/ a default
1853 // precision of 10, but since it will just be .00000000, it's
1857 private static string FormatGeneral (byte value, int precision, NumberFormatInfo nfi, bool upper) {
1858 return FormatGeneral ((uint)value, precision, nfi, upper);
1861 private static string FormatGeneral (short value, int precision, NumberFormatInfo nfi, bool upper) {
1862 return FormatGeneral ((int)value, precision, nfi, upper);
1865 private static string FormatGeneral (int value, int precision, NumberFormatInfo nfi, bool upper)
1867 bool negative = (value < 0);
1868 char[] tmp = new char [maxIntLength];
1870 int position = maxIntLength;
1872 // ugly, but doing it since abs(Int32.MinValue) > Int.MaxValue
1873 uint number = (negative) ? (uint)((-(value + 1)) + 1) : (uint)value;
1875 // get number into a buffer, going to be doing this no matter what
1878 tmp[--position] = digitLowerTable[-(value % 10)];
1881 } else value = -value;
1883 while (value >= 10) {
1884 tmp[--position] = digitLowerTable[(value % 10)];
1889 tmp[--position] = digitLowerTable[value];
1891 // integral values are formatted using fixed point when precision
1892 // is not specified. But also trailing decimal point and zeros are
1893 // discared. So for int's it will always be .00, so just compute
1894 // here and save the call to FormatFixedPoint & trim.
1895 if (precision <= 0 || exponent < precision) {
1897 tmp[--position] = '-';
1899 return new string (tmp, position, (maxIntLength - position));
1902 // else our exponent was > precision, use exponential format
1903 // precision = number of digits to show.
1904 int idx = 0, pow = 1;
1907 position = maxIntLength;
1909 // Loop through while our number is less than the 10 ^ precision, then
1910 // add 5 to that to round it out, and keep continuing
1911 while (idx++ <= precision)
1914 while (number > pow) {
1915 tmp[--position] = digitLowerTable[(number % 10)];
1922 while (number >= 10) {
1923 tmp[--position] = digitLowerTable[(number % 10)];
1928 tmp[--position] = digitLowerTable[number];
1930 // finally, make our final buffer, at least precision + 6 for 'E+XX' and '-'
1931 // and reuse pow for size
1934 pow = precision + 6;
1935 char[] buffy = new char[pow];
1938 buffy[position++] = '-';
1940 buffy[position++] = tmp[idx++];
1941 buffy[position] = '.';
1943 // for the remaining precisions copy over rounded tmp
1945 while (precision-- > 0)
1946 buffy[++position] = tmp[idx++];
1948 // get rid of ending zeros
1949 while (buffy[position] == '0')
1952 // if we backed up all the way to the ., over write it
1953 if (buffy[position] != '.')
1956 // ints can only be +, e or E depending on format, plus XX
1957 buffy[position++] = upper ? 'E' : 'e';
1958 buffy[position++] = '+';
1960 if (exponent >= 10) {
1961 buffy[position++] = digitLowerTable[(exponent / 10)];
1962 buffy[position++] = digitLowerTable[(exponent % 10)];
1964 buffy[position++] = '0';
1965 buffy[position++] = digitLowerTable[exponent];
1968 return new string (buffy, 0, position);
1971 private static string FormatGeneral (long value, int precision, NumberFormatInfo nfi, bool upper)
1973 bool negative = (value < 0);
1974 char[] tmp = new char [maxLongLength];
1976 int position = maxLongLength;
1978 // ugly, but doing it since abs(Int32.MinValue) > Int.MaxValue
1979 ulong number = (negative) ? (ulong)(-(value + 1) + 1) : (ulong)value;
1981 // get number into a buffer, going to be doing this no matter what
1984 tmp[--position] = digitLowerTable[-(value % 10)];
1986 } else value = -value;
1988 while (value >= 10) {
1989 tmp[--position] = digitLowerTable[(value % 10)];
1993 tmp[--position] = digitLowerTable[value];
1994 exponent = (maxLongLength - position) - 1;
1996 // integral values are formatted using fixed point when precision
1997 // is not specified. But also trailing decimal point and zeros are
1998 // discared. So for int's it will always be .00, so just compute
1999 // here and save the call to FormatFixedPoint & trim.
2000 if (precision <= 0 || exponent < precision) {
2002 tmp[--position] = '-';
2004 return new string (tmp, position, (maxLongLength - position));
2007 // else our exponent was > precision, use exponential format
2008 // precision = number of digits to show.
2013 position = maxLongLength;
2015 // Loop through while our number is less than the 10 ^ precision, then
2016 // add 5 to that to round it out, and keep continuing
2017 while (idx++ <= precision)
2020 while (number > pow) {
2021 tmp[--position] = digitLowerTable[(number % 10)];
2028 while (number >= 10) {
2029 tmp[--position] = digitLowerTable[(number % 10)];
2034 tmp[--position] = digitLowerTable[number];
2036 // finally, make our final buffer, at least precision + 6 for 'E+XX' and '-'
2037 // and reuse pow for size
2040 pow = (ulong)precision + 6;
2041 char[] buffy = new char[pow];
2044 buffy[position++] = '-';
2046 buffy[position++] = tmp[idx++];
2047 buffy[position] = '.';
2049 // for the remaining precisions copy over rounded tmp
2051 while (precision-- > 0)
2052 buffy[++position] = tmp[idx++];
2054 // get rid of ending zeros
2055 while (buffy[position] == '0')
2058 // if we backed up all the way to the ., over write it
2059 if (buffy[position] != '.')
2062 // ints can only be +, e or E depending on format, plus XX
2063 buffy[position++] = upper ? 'E' : 'e';
2064 buffy[position++] = '+';
2066 if (exponent >= 10) {
2067 buffy[position++] = digitLowerTable[(exponent / 10)];
2068 buffy[position++] = digitLowerTable[(exponent % 10)];
2070 buffy[position++] = '0';
2071 buffy[position++] = digitLowerTable[exponent];
2074 return new string (buffy, 0, position);
2077 private static string FormatGeneral (sbyte value, int precision, NumberFormatInfo nfi, bool upper) {
2078 return FormatGeneral ((int)value, precision, nfi, upper);
2081 private static string FormatGeneral (ushort value, int precision, NumberFormatInfo nfi, bool upper) {
2082 return FormatGeneral ((uint)value, precision, nfi, upper);
2085 private static string FormatGeneral (uint value, int precision, NumberFormatInfo nfi, bool upper)
2087 char[] tmp = new char [maxIntLength];
2089 int position = maxIntLength;
2090 uint number = value;
2092 // get number into a buffer, going to be doing this no matter what
2093 while (value >= 10) {
2094 tmp[--position] = digitLowerTable[(value % 10)];
2098 tmp[--position] = digitLowerTable[value];
2099 exponent = (maxIntLength - position) - 1;
2101 // integral values are formatted using fixed point when precision
2102 // is not specified. But also trailing decimal point and zeros are
2103 // discared. So for int's it will always be .00, so just compute
2104 // here and save the call to FormatFixedPoint & trim.
2105 if (precision <= 0 || exponent < precision)
2106 return new string (tmp, position, (maxIntLength - position));
2108 // else our exponent was > precision, use exponential format
2109 // precision = number of digits to show.
2110 int idx = 0, pow = 1;
2113 position = maxIntLength;
2115 // Loop through while our number is less than the 10 ^ precision, then
2116 // add 5 to that to round it out, and keep continuing
2117 while (idx++ <= precision)
2120 while (number > pow) {
2121 tmp[--position] = digitLowerTable[(number % 10)];
2128 while (number >= 10) {
2129 tmp[--position] = digitLowerTable[(number % 10)];
2134 tmp[--position] = digitLowerTable[number];
2136 // finally, make our final buffer, at least precision + 6 for 'E+XX' and '-'
2137 // and reuse pow for size
2140 pow = precision + 6;
2141 char[] buffy = new char[pow];
2143 buffy[position++] = tmp[idx++];
2144 buffy[position] = '.';
2146 // for the remaining precisions copy over rounded tmp
2148 while (precision-- > 0)
2149 buffy[++position] = tmp[idx++];
2151 // get rid of ending zeros
2152 while (buffy[position] == '0')
2155 // if we backed up all the way to the ., over write it
2156 if (buffy[position] != '.')
2159 // ints can only be +, e or E depending on format, plus XX
2160 buffy[position++] = upper ? 'E' : 'e';
2161 buffy[position++] = '+';
2163 if (exponent >= 10) {
2164 buffy[position++] = digitLowerTable[(exponent / 10)];
2165 buffy[position++] = digitLowerTable[(exponent % 10)];
2167 buffy[position++] = '0';
2168 buffy[position++] = digitLowerTable[exponent];
2171 return new string (buffy, 0, position);
2174 private static string FormatGeneral (ulong value, int precision, NumberFormatInfo nfi, bool upper)
2176 char[] tmp = new char [maxLongLength];
2178 int position = maxLongLength;
2179 ulong number = value;
2181 // get number into a buffer, going to be doing this no matter what
2182 while (value >= 10) {
2183 tmp[--position] = digitLowerTable[(value % 10)];
2188 tmp[--position] = digitLowerTable[value];
2190 // integral values are formatted using fixed point when precision
2191 // is not specified. But also trailing decimal point and zeros are
2192 // discared. So for int's it will always be .00, so just compute
2193 // here and save the call to FormatFixedPoint & trim.
2194 if (precision <= 0 || exponent < precision)
2195 return new string (tmp, position, (maxLongLength - position));
2197 // else our exponent was > precision, use exponential format
2198 // precision = number of digits to show.
2203 position = maxLongLength;
2205 // Loop through while our number is less than the 10 ^ precision, then
2206 // add 5 to that to round it out, and keep continuing
2207 while (idx++ <= precision)
2210 while (number > pow) {
2211 tmp[--position] = digitLowerTable[(number % 10)];
2218 while (number >= 10) {
2219 tmp[--position] = digitLowerTable[(number % 10)];
2224 tmp[--position] = digitLowerTable[number];
2226 // finally, make our final buffer, at least precision + 6 for 'E+XX' and '-'
2227 // and reuse pow for size
2230 pow = (ulong)precision + 6;
2231 char[] buffy = new char[pow];
2233 buffy[position++] = tmp[idx++];
2234 buffy[position] = '.';
2236 // for the remaining precisions copy over rounded tmp
2238 while (precision-- > 0)
2239 buffy[++position] = tmp[idx++];
2241 // get rid of ending zeros
2242 while (buffy[position] == '0')
2245 // if we backed up all the way to the ., over write it
2246 if (buffy[position] != '.')
2249 // ints can only be +, e or E depending on format, plus XX
2250 buffy[position++] = upper ? 'E' : 'e';
2251 buffy[position++] = '+';
2253 if (exponent >= 10) {
2254 buffy[position++] = digitLowerTable[(exponent / 10)];
2255 buffy[position++] = digitLowerTable[(exponent % 10)];
2257 buffy[position++] = '0';
2258 buffy[position++] = digitLowerTable[exponent];
2261 return new string (buffy, 0, position);
2264 // ============ Format Number ============ //
2267 // Used for strings in the following form "[-]d,ddd,ddd.dd...d"
2268 // The minus sign only appears if it is negative. At least one
2269 // non-zero digit preceeds the decimal separator. The precision
2270 // specifier determines the number of decimal places. If it is
2271 // not given, use NumberFormatInfo.NumberDecimalDigits.
2272 // The NumberGroupSizes, NumberGroupSeparator, and NumberDecimalSeparator
2273 // members of NumberFormatInfo supply the size and separator
2274 // for digit groupings. See IFormattable.
2276 // The group sizes is an array of ints that determine the grouping
2277 // of numbers. All digits are in the range 1-9, with the last digit
2278 // being between 0-9. The number formats the string backwards, with
2279 // the last digit being the group size for the rest of (leftmost) the
2280 // the string, 0 being none.
2283 // groupSizes = { 3, 2, 1, 0 };
2284 // int n = 1234567890 => "1234,5,67,890"
2285 // groupSizes = { 3, 2, 1 };
2286 // int n = 1234567890 => "1,2,3,4,5,67,890"
2287 // groupSizes = { 2, 0 };
2288 // int n = 1234567890 => "1234567,90";
2290 // Not too difficult, jsut keep track of where you are in the array
2291 // and when to print the separator
2293 // The max size of the buffer is assume we have a separator every
2294 // number, plus the precision on the end, plus a spot for the negative
2295 // and a spot for decimal separator.
2298 private static string FormatNumber (byte value, int precision, NumberFormatInfo nfi) {
2299 return FormatNumber ((uint)value, precision, nfi);
2302 private static string FormatNumber (short value, int precision, NumberFormatInfo nfi) {
2303 return FormatNumber ((int)value, precision, nfi);
2306 private static string FormatNumber (int value, int precision, NumberFormatInfo nfi)
2309 char[] groupSeparator = nfi.NumberGroupSeparator.ToCharArray ();
2310 char[] decimalSeparator = nfi.NumberDecimalSeparator.ToCharArray ();
2311 int[] groupSizes = nfi.NumberGroupSizes;
2313 int padding = (precision >= 0) ? precision : nfi.NumberDecimalDigits;
2314 int size = maxIntLength + (maxIntLength * groupSeparator.Length) + padding +
2315 decimalSeparator.Length + 2;
2316 char[] buffy = new char[size];
2317 int position = size;
2318 bool negative = (value < 0);
2320 // right pad it w/ precision 0's
2321 while (padding-- > 0)
2322 buffy[--position] = '0';
2324 // put on decimal separator
2325 if (position != size) {
2326 i = decimalSeparator.Length;
2328 buffy[--position] = decimalSeparator[--i];
2332 // loop through, keeping track of where you are in the
2333 // group sizes array and putting out the group separator
2336 k = groupSizes[j++];
2338 // negative hack for numbers past MinValue
2341 buffy[--position] = digitLowerTable[-(value % 10)];
2342 value = value / -10;
2345 i = groupSeparator.Length;
2347 buffy[--position] = groupSeparator[--i];
2350 k = (j < groupSizes.Length) ?
2351 groupSizes[j++] : groupSizes[(groupSizes.Length - 1)];
2353 } else value = -value;
2355 while (value >= 10) {
2356 buffy[--position] = digitLowerTable[(value % 10)];
2360 i = groupSeparator.Length;
2362 buffy[--position] = groupSeparator[--i];
2365 k = (j < groupSizes.Length) ?
2366 groupSizes[j++] : groupSizes[(groupSizes.Length - 1)];
2370 buffy[--position] = digitLowerTable[value];
2373 buffy[--position] = '-';
2375 return new string (buffy, position, (size - position));
2378 private static string FormatNumber (long value, int precision, NumberFormatInfo nfi)
2381 char[] groupSeparator = nfi.NumberGroupSeparator.ToCharArray ();
2382 char[] decimalSeparator = nfi.NumberDecimalSeparator.ToCharArray ();
2383 int[] groupSizes = nfi.NumberGroupSizes;
2385 int padding = (precision >= 0) ? precision : nfi.NumberDecimalDigits;
2386 int size = maxLongLength + (maxLongLength * groupSeparator.Length) + padding +
2387 decimalSeparator.Length + 2;
2388 char[] buffy = new char[size];
2389 int position = size;
2390 bool negative = (value < 0);
2392 // right pad it w/ precision 0's
2393 while (padding-- > 0)
2394 buffy[--position] = '0';
2396 // put on decimal separator
2397 if (position != size) {
2398 i = decimalSeparator.Length;
2400 buffy[--position] = decimalSeparator[--i];
2404 // loop through, keeping track of where you are in the
2405 // group sizes array and putting out the group separator
2408 k = groupSizes[j++];
2410 // negative hack for numbers past MinValue
2413 buffy[--position] = digitLowerTable[-(value % 10)];
2414 value = value / -10;
2417 i = groupSeparator.Length;
2419 buffy[--position] = groupSeparator[--i];
2422 k = (j < groupSizes.Length) ?
2423 groupSizes[j++] : groupSizes[(groupSizes.Length - 1)];
2425 } else value = -value;
2427 while (value >= 10) {
2428 buffy[--position] = digitLowerTable[(value % 10)];
2432 i = groupSeparator.Length;
2434 buffy[--position] = groupSeparator[--i];
2437 k = (j < groupSizes.Length) ?
2438 groupSizes[j++] : groupSizes[(groupSizes.Length - 1)];
2442 buffy[--position] = digitLowerTable[value];
2445 buffy[--position] = '-';
2447 return new string (buffy, position, (size - position));
2450 private static string FormatNumber (sbyte value, int precision, NumberFormatInfo nfi) {
2451 return FormatNumber ((int)value, precision, nfi);
2454 private static string FormatNumber (ushort value, int precision, NumberFormatInfo nfi) {
2455 return FormatNumber ((uint)value, precision, nfi);
2458 private static string FormatNumber (uint value, int precision, NumberFormatInfo nfi)
2461 char[] groupSeparator = nfi.NumberGroupSeparator.ToCharArray ();
2462 char[] decimalSeparator = nfi.NumberDecimalSeparator.ToCharArray ();
2463 int[] groupSizes = nfi.NumberGroupSizes;
2465 int padding = (precision >= 0) ? precision : nfi.NumberDecimalDigits;
2466 int size = maxIntLength + (maxIntLength * groupSeparator.Length) + padding +
2467 decimalSeparator.Length + 2;
2468 char[] buffy = new char[size];
2469 int position = size;
2471 // right pad it w/ precision 0's
2472 while (padding-- > 0)
2473 buffy[--position] = '0';
2475 // put on decimal separator
2476 if (position != size) {
2477 i = decimalSeparator.Length;
2479 buffy[--position] = decimalSeparator[--i];
2483 // loop through, keeping track of where you are in the
2484 // group sizes array and putting out the group separator
2487 k = groupSizes[j++];
2489 while (value >= 10) {
2490 buffy[--position] = digitLowerTable[(value % 10)];
2494 i = groupSeparator.Length;
2496 buffy[--position] = groupSeparator[--i];
2499 k = (j < groupSizes.Length) ?
2500 groupSizes[j++] : groupSizes[(groupSizes.Length - 1)];
2504 buffy[--position] = digitLowerTable[value];
2506 return new string (buffy, position, (size - position));
2509 private static string FormatNumber (ulong value, int precision, NumberFormatInfo nfi)
2512 char[] groupSeparator = nfi.NumberGroupSeparator.ToCharArray ();
2513 char[] decimalSeparator = nfi.NumberDecimalSeparator.ToCharArray ();
2514 int[] groupSizes = nfi.NumberGroupSizes;
2516 int padding = (precision >= 0) ? precision : nfi.NumberDecimalDigits;
2517 int size = maxLongLength + (maxLongLength * groupSeparator.Length) + padding +
2518 decimalSeparator.Length + 2;
2519 char[] buffy = new char[size];
2520 int position = size;
2522 // right pad it w/ precision 0's
2523 while (padding-- > 0)
2524 buffy[--position] = '0';
2526 // put on decimal separator
2527 if (position != size) {
2528 i = decimalSeparator.Length;
2530 buffy[--position] = decimalSeparator[--i];
2534 // loop through, keeping track of where you are in the
2535 // group sizes array and putting out the group separator
2538 k = groupSizes[j++];
2540 while (value >= 10) {
2541 buffy[--position] = digitLowerTable[(value % 10)];
2545 i = groupSeparator.Length;
2547 buffy[--position] = groupSeparator[--i];
2550 k = (j < groupSizes.Length) ?
2551 groupSizes[j++] : groupSizes[(groupSizes.Length - 1)];
2555 buffy[--position] = digitLowerTable[value];
2557 return new string (buffy, position, (size - position));
2560 // ============ Percent Formatting ============ //
2563 // Percent Format: Used for strings containing a percentage. The
2564 // PercentSymbol, PercentGroupSizes, PercentGroupSeparator, and
2565 // PercentDecimalSeparator members of a NumberFormatInfo supply
2566 // the Percent symbol, size and separator for digit groupings, and
2567 // decimal separator, respectively.
2568 // PercentNegativePattern and PercentPositivePattern determine the
2569 // symbols used to represent negative and positive values. For example,
2570 // a negative value may be prefixed with a minus sign, or enclosed in
2572 // If no precision is specified, the number of decimal places in the result
2573 // is set by NumberFormatInfo.PercentDecimalDigits. Results are
2574 // rounded to the nearest representable value when necessary.
2575 // The result is scaled by 100 (.99 becomes 99%).
2577 // The pattern of the number determines how the output looks, where
2578 // the percent sign goes, where the negative sign goes, etc.
2579 // IFormattable documentation lists the patterns and their values,
2580 // I have them commented out in the switch statement
2583 private static string FormatPercent (byte value, int precision, NumberFormatInfo nfi)
2585 return FormatPercent ((uint)value, precision, nfi);
2588 private static string FormatPercent (short value, int precision, NumberFormatInfo nfi)
2590 return FormatPercent ((int)value, precision, nfi);
2593 private static string FormatPercent (int value, int precision, NumberFormatInfo nfi)
2596 bool negative = (value < 0);
2598 char[] groupSeparator = nfi.PercentGroupSeparator.ToCharArray ();
2599 char[] decimalSeparator = nfi.PercentDecimalSeparator.ToCharArray ();
2600 char[] percentSymbol = nfi.PercentSymbol.ToCharArray ();
2601 int[] groupSizes = nfi.PercentGroupSizes;
2602 int pattern = negative ? nfi.PercentNegativePattern : nfi.PercentPositivePattern;
2603 int symbolLength = percentSymbol.Length;
2605 int padding = (precision >= 0) ? precision : nfi.PercentDecimalDigits;
2606 int size = maxIntLength + (groupSeparator.Length * maxIntLength) + padding + 2 +
2607 decimalSeparator.Length + symbolLength;
2608 char[] buffy = new char[size];
2609 int position = size;
2611 // set up the pattern from IFormattible
2618 buffy[--position] = percentSymbol[--i];
2620 buffy[--position] = ' ';
2624 buffy[--position] = percentSymbol[--i];
2635 buffy[--position] = percentSymbol[--i];
2637 buffy[--position] = ' ';
2641 buffy[--position] = percentSymbol[--i];
2649 // right pad it w/ precision 0's
2650 while (padding-- > 0)
2651 buffy[--position] = '0';
2653 // put on decimal separator if we moved over and put a 0
2654 if (position < size && buffy[position] == '0') {
2655 i = decimalSeparator.Length;
2657 buffy[--position] = decimalSeparator[--i];
2661 // loop through, keeping track of where you are in the
2662 // group sizes array and putting out the group separator
2665 k = groupSizes[j++];
2667 // all values are multiplied by 100, so tack on two 0's
2669 for (int c = 0; c < 2; c++) {
2670 buffy[--position] = '0';
2673 i = groupSeparator.Length;
2675 buffy[--position] = groupSeparator[--i];
2678 k = (j < groupSizes.Length) ?
2679 groupSizes[j++] : groupSizes[(groupSizes.Length - 1)];
2683 // negative hack for numbers past MinValue
2686 buffy[--position] = digitLowerTable[-(value % 10)];
2687 value = value / -10;
2690 i = groupSeparator.Length;
2692 buffy[--position] = groupSeparator[--i];
2695 k = (j < groupSizes.Length) ?
2696 groupSizes[j++] : groupSizes[(groupSizes.Length - 1)];
2698 } else value = -value;
2700 while (value >= 10) {
2701 buffy[--position] = digitLowerTable[(value % 10)];
2705 i = groupSeparator.Length;
2707 buffy[--position] = groupSeparator[--i];
2710 k = (j < groupSizes.Length) ?
2711 groupSizes[j++] : groupSizes[(groupSizes.Length - 1)];
2715 buffy[--position] = digitLowerTable[value];
2717 // end the pattern on the left hand side
2723 buffy[--position] = '-';
2726 buffy[--position] = '-';
2730 buffy[--position] = percentSymbol[--i];
2732 buffy[--position] = '-';
2744 buffy[--position] = percentSymbol[--i];
2750 return new string (buffy, position, (size - position));
2753 private static string FormatPercent (long value, int precision, NumberFormatInfo nfi)
2756 bool negative = (value < 0);
2758 char[] groupSeparator = nfi.PercentGroupSeparator.ToCharArray ();
2759 char[] decimalSeparator = nfi.PercentDecimalSeparator.ToCharArray ();
2760 char[] percentSymbol = nfi.PercentSymbol.ToCharArray ();
2761 int[] groupSizes = nfi.PercentGroupSizes;
2762 int pattern = negative ? nfi.PercentNegativePattern : nfi.PercentPositivePattern;
2763 int symbolLength = percentSymbol.Length;
2765 int padding = (precision >= 0) ? precision : nfi.PercentDecimalDigits;
2766 int size = maxLongLength + (groupSeparator.Length * maxLongLength) + padding + 2 +
2767 decimalSeparator.Length + symbolLength;
2768 char[] buffy = new char[size];
2769 int position = size;
2771 // set up the pattern from IFormattible
2778 buffy[--position] = percentSymbol[--i];
2780 buffy[--position] = ' ';
2784 buffy[--position] = percentSymbol[--i];
2795 buffy[--position] = percentSymbol[--i];
2797 buffy[--position] = ' ';
2801 buffy[--position] = percentSymbol[--i];
2809 // right pad it w/ precision 0's
2810 while (padding-- > 0)
2811 buffy[--position] = '0';
2813 // put on decimal separator if we moved over and put a 0
2814 if (position < size && buffy[position] == '0') {
2815 i = decimalSeparator.Length;
2817 buffy[--position] = decimalSeparator[--i];
2821 // loop through, keeping track of where you are in the
2822 // group sizes array and putting out the group separator
2825 k = groupSizes[j++];
2827 // all values are multiplied by 100, so tack on two 0's
2829 for (int c = 0; c < 2; c++) {
2830 buffy[--position] = '0';
2833 i = groupSeparator.Length;
2835 buffy[--position] = groupSeparator[--i];
2838 k = (j < groupSizes.Length) ?
2839 groupSizes[j++] : groupSizes[(groupSizes.Length - 1)];
2843 // negative hack for numbers past MinValue
2846 buffy[--position] = digitLowerTable[-(value % 10)];
2847 value = value / -10;
2850 i = groupSeparator.Length;
2852 buffy[--position] = groupSeparator[--i];
2855 k = (j < groupSizes.Length) ?
2856 groupSizes[j++] : groupSizes[(groupSizes.Length - 1)];
2858 } else value = -value;
2860 while (value >= 10) {
2861 buffy[--position] = digitLowerTable[(value % 10)];
2865 i = groupSeparator.Length;
2867 buffy[--position] = groupSeparator[--i];
2870 k = (j < groupSizes.Length) ?
2871 groupSizes[j++] : groupSizes[(groupSizes.Length - 1)];
2875 buffy[--position] = digitLowerTable[value];
2877 // end the pattern on the left hand side
2883 buffy[--position] = '-';
2886 buffy[--position] = '-';
2890 buffy[--position] = percentSymbol[--i];
2892 buffy[--position] = '-';
2904 buffy[--position] = percentSymbol[--i];
2910 return new string (buffy, position, (size - position));
2913 private static string FormatPercent (sbyte value, int precision, NumberFormatInfo nfi)
2915 return FormatPercent ((int)value, precision, nfi);
2918 private static string FormatPercent (ushort value, int precision, NumberFormatInfo nfi)
2920 return FormatPercent ((uint)value, precision, nfi);
2923 private static string FormatPercent (uint value, int precision, NumberFormatInfo nfi)
2927 char[] groupSeparator = nfi.PercentGroupSeparator.ToCharArray ();
2928 char[] decimalSeparator = nfi.PercentDecimalSeparator.ToCharArray ();
2929 char[] percentSymbol = nfi.PercentSymbol.ToCharArray ();
2930 int[] groupSizes = nfi.PercentGroupSizes;
2931 int pattern = nfi.PercentPositivePattern;
2932 int symbolLength = percentSymbol.Length;
2934 int padding = (precision >= 0) ? precision : nfi.PercentDecimalDigits;
2935 int size = maxIntLength + (groupSeparator.Length * maxIntLength) + padding + 2 +
2936 decimalSeparator.Length + symbolLength;
2937 char[] buffy = new char[size];
2938 int position = size;
2940 // set up the pattern from IFormattible
2945 buffy[--position] = percentSymbol[--i];
2947 buffy[--position] = ' ';
2951 buffy[--position] = percentSymbol[--i];
2958 // right pad it w/ precision 0's
2959 while (padding-- > 0)
2960 buffy[--position] = '0';
2962 // put on decimal separator if we moved over and put a 0
2963 if (position < size && buffy[position] == '0') {
2964 i = decimalSeparator.Length;
2966 buffy[--position] = decimalSeparator[--i];
2970 // loop through, keeping track of where you are in the
2971 // group sizes array and putting out the group separator
2974 k = groupSizes[j++];
2977 for (int c = 0; c < 2; c++) {
2978 buffy[--position] = '0';
2981 i = groupSeparator.Length;
2983 buffy[--position] = groupSeparator[--i];
2986 k = (j < groupSizes.Length) ?
2987 groupSizes[j++] : groupSizes[(groupSizes.Length - 1)];
2991 while (value >= 10) {
2992 buffy[--position] = digitLowerTable[(value % 10)];
2996 i = groupSeparator.Length;
2998 buffy[--position] = groupSeparator[--i];
3001 k = (j < groupSizes.Length) ?
3002 groupSizes[j++] : groupSizes[(groupSizes.Length - 1)];
3006 buffy[--position] = digitLowerTable[value];
3016 buffy[--position] = percentSymbol[--i];
3021 return new string (buffy, position, (size - position));
3024 private static string FormatPercent (ulong value, int precision, NumberFormatInfo nfi)
3028 char[] groupSeparator = nfi.PercentGroupSeparator.ToCharArray ();
3029 char[] decimalSeparator = nfi.PercentDecimalSeparator.ToCharArray ();
3030 char[] percentSymbol = nfi.PercentSymbol.ToCharArray ();
3031 int[] groupSizes = nfi.PercentGroupSizes;
3032 int pattern = nfi.PercentPositivePattern;
3033 int symbolLength = percentSymbol.Length;
3035 int padding = (precision >= 0) ? precision : nfi.PercentDecimalDigits;
3036 int size = maxLongLength + (groupSeparator.Length * maxLongLength) + padding + 2 +
3037 decimalSeparator.Length + symbolLength;
3038 char[] buffy = new char[size];
3039 int position = size;
3041 // set up the pattern from IFormattible
3046 buffy[--position] = percentSymbol[--i];
3048 buffy[--position] = ' ';
3052 buffy[--position] = percentSymbol[--i];
3059 // right pad it w/ precision 0's
3060 while (padding-- > 0)
3061 buffy[--position] = '0';
3063 // put on decimal separator if we moved over and put a 0
3064 if (position < size && buffy[position] == '0') {
3065 i = decimalSeparator.Length;
3067 buffy[--position] = decimalSeparator[--i];
3071 // loop through, keeping track of where you are in the
3072 // group sizes array and putting out the group separator
3075 k = groupSizes[j++];
3078 for (int c = 0; c < 2; c++) {
3079 buffy[--position] = '0';
3082 i = groupSeparator.Length;
3084 buffy[--position] = groupSeparator[--i];
3087 k = (j < groupSizes.Length) ?
3088 groupSizes[j++] : groupSizes[(groupSizes.Length - 1)];
3092 while (value >= 10) {
3093 buffy[--position] = digitLowerTable[(value % 10)];
3097 i = groupSeparator.Length;
3099 buffy[--position] = groupSeparator[--i];
3102 k = (j < groupSizes.Length) ?
3103 groupSizes[j++] : groupSizes[(groupSizes.Length - 1)];
3107 buffy[--position] = digitLowerTable[value];
3117 buffy[--position] = percentSymbol[--i];
3122 return new string (buffy, position, (size - position));
3125 // ============ Format Hexadecimal ============ //
3128 // For strings in base 16. Only valid w/ integers. Precision
3129 // specifies number of digits in the string, if it specifies
3130 // more digits than we need, left pad w/ 0's. The case of the
3131 // the format specifier 'X' or 'x' determines lowercase or
3132 // capital digits in the output.
3134 // Whew. Straight forward Hex formatting, however only
3135 // go 8 places max when dealing with an int (not counting
3136 // precision padding) and 16 when dealing with a long. This
3137 // is to cut off the loop when dealing with negative values,
3138 // which will loop forever when you hit -1;
3141 private static string FormatHexadecimal (byte value, int precision, bool upper)
3143 if (precision < 0) precision = 0;
3144 int size = maxByteLength + precision;
3145 char[] buffy = new char[size];
3146 char[] table = upper ? digitUpperTable : digitLowerTable;
3147 int position = size;
3148 ushort mask = (1 << 4) - 1;
3150 // loop through right to left, shifting and looking up
3151 // our value. Don't worry about negative
3153 buffy[--position] = table[(value & mask)];
3154 value = (byte)(value >> 4);
3155 } while (value != 0);
3157 // pad w/ 0's if they want more length, if not, ignore
3158 precision -= (size - position);
3159 while (precision > 0 && position > 1) {
3160 buffy[--position] = '0';
3164 return new string(buffy, position, (size - position));
3167 private static string FormatHexadecimal (short value, int precision, bool upper)
3169 if (precision < 0) precision = 0;
3170 int size = maxShortLength + precision;
3171 char[] buffy = new char[size];
3172 char[] table = upper ? digitUpperTable : digitLowerTable;
3173 int position = size;
3174 short mask = (1 << 4) - 1;
3176 // loop through right to left, shifting and looking up
3177 // our value. If value is negavite stop after 4 F's
3179 buffy[--position] = table[(value & mask)];
3180 value = (short)(value >> 4);
3181 } while (value != 0 && position > (size - 4));
3183 // pad w/ 0's if they want more length, if not, ignore
3184 precision -= (size - position);
3185 while (precision > 0 && position > 1) {
3186 buffy[--position] = '0';
3190 return new string(buffy, position, (size - position));
3193 private static string FormatHexadecimal (int value, int precision, bool upper)
3195 if (precision < 0) precision = 0;
3196 int size = maxIntLength + precision;
3197 char[] buffy = new char[size];
3198 char[] table = upper ? digitUpperTable : digitLowerTable;
3199 int position = size;
3200 int mask = (1 << 4) - 1;
3202 // loop through right to left, shifting and looking up
3203 // our value. If value is negavite stop after 8 F's
3205 buffy[--position] = table[(value & mask)];
3207 } while (value != 0 && position > (size - 8));
3209 // pad w/ 0's if they want more length, if not, ignore
3210 precision -= (size - position);
3211 while (precision > 0 && position > 1) {
3212 buffy[--position] = '0';
3216 return new string(buffy, position, (size - position));
3219 private static string FormatHexadecimal (long value, int precision, bool upper)
3221 if (precision < 0) precision = 0;
3222 int size = maxLongLength + precision;
3223 char[] buffy = new char[size];
3224 char[] table = upper ? digitUpperTable : digitLowerTable;
3225 int position = size;
3226 long mask = (1 << 4) - 1;
3228 // loop through right to left, shifting and looking up
3229 // our value. If value is negavite stop after 16 F's
3231 buffy[--position] = table[(value & mask)];
3233 } while (value != 0 && position > (size - 16));
3235 // pad w/ 0's if they want more length, if not, ignore
3236 precision -= (size - position);
3237 while (precision > 0 && position > 1) {
3238 buffy[--position] = '0';
3242 return new string(buffy, position, (size - position));
3245 private static string FormatHexadecimal (sbyte value, int precision, bool upper)
3247 if (precision < 0) precision = 0;
3248 int size = maxByteLength + precision;
3249 char[] buffy = new char[size];
3250 char[] table = upper ? digitUpperTable : digitLowerTable;
3251 int position = size;
3252 short mask = (1 << 4) - 1;
3254 // loop through right to left, shifting and looking up
3255 // our value. If value is negavite stop after 2 F's
3257 buffy[--position] = table[(value & mask)];
3258 value = (sbyte)(value >> 4);
3259 } while (value != 0 && position > (size - 2));
3261 // pad w/ 0's if they want more length, if not, ignore
3262 precision -= (size - position);
3263 while (precision > 0 && position > 1) {
3264 buffy[--position] = '0';
3268 return new string(buffy, position, (size - position));
3271 private static string FormatHexadecimal (ushort value, int precision, bool upper)
3273 if (precision < 0) precision = 0;
3274 int size = maxShortLength + precision;
3275 char[] buffy = new char[size];
3276 char[] table = upper ? digitUpperTable : digitLowerTable;
3277 int position = size;
3278 int mask = (1 << 4) - 1;
3280 // loop through right to left, shifting and looking up
3281 // our value. Don't worry about negative
3283 buffy[--position] = table[(value & mask)];
3284 value = (ushort)(value >> 4);
3285 } while (value != 0);
3287 // pad w/ 0's if they want more length, if not, ignore
3288 precision -= (size - position);
3289 while (precision > 0 && position > 1) {
3290 buffy[--position] = '0';
3294 return new string(buffy, position, (size - position));
3297 private static string FormatHexadecimal (uint value, int precision, bool upper)
3299 if (precision < 0) precision = 0;
3300 int size = maxIntLength + precision;
3301 char[] buffy = new char[size];
3302 char[] table = upper ? digitUpperTable : digitLowerTable;
3303 int position = size;
3304 uint mask = (1 << 4) - 1;
3306 // loop through right to left, shifting and looking up
3307 // our value. Don't worry about negative
3309 buffy[--position] = table[(value & mask)];
3311 } while (value != 0);
3313 // pad w/ 0's if they want more length, if not, ignore
3314 precision -= (size - position);
3315 while (precision > 0 && position > 1) {
3316 buffy[--position] = '0';
3320 return new string(buffy, position, (size - position));
3323 private static string FormatHexadecimal (ulong value, int precision, bool upper)
3325 if (precision < 0) precision = 0;
3326 int size = maxLongLength + precision;
3327 char[] buffy = new char[size];
3328 char[] table = upper ? digitUpperTable : digitLowerTable;
3329 int position = size;
3330 ulong mask = (1 << 4) - 1;
3332 // loop through right to left, shifting and looking up
3333 // our value. Don't worry about negative
3335 buffy[--position] = table[value & mask];
3337 } while (value != 0);
3339 // pad w/ 0's if they want more length, if not, ignore
3340 precision -= (size - position);
3341 while (precision > 0 && position > 1) {
3342 buffy[--position] = '0';
3346 return new string(buffy, position, (size - position));