2 // System.FloatingPointFormatter.cs
5 // Pedro Martinez Julia <yoros@wanadoo.es>
6 // Jon Skeet <skeet@pobox.com>
7 // Sebastien Pouliot <sebastien@ximian.com>
9 // Copyright (C) 2003 Pedro MartÃez Juliá <yoros@wanadoo.es>
10 // Copyright (C) 2004 Jon Skeet
11 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 using System.Collections;
36 using System.Globalization;
41 internal class FloatingPointFormatter {
47 public int dec_len_min;
53 public FloatingPointFormatter
54 (double p, double p10, int dec_len, int dec_len_min,
55 double p2, double p102, int dec_len2, int dec_len_min2) {
59 format1.dec_len = dec_len;
60 format1.dec_len_min = dec_len_min;
64 format2.dec_len = dec_len2;
65 format2.dec_len_min = dec_len_min2;
68 public string GetStringFrom
69 (string format, NumberFormatInfo nfi, double value) {
71 if (format == null || format == "") {
75 nfi = NumberFormatInfo.CurrentInfo;
77 if (Double.IsNaN(value)) {
80 if (Double.IsNegativeInfinity(value)) {
81 return nfi.NegativeInfinitySymbol;
83 if (Double.IsPositiveInfinity(value)) {
84 return nfi.PositiveInfinitySymbol;
89 if (!ParseFormat(format, out specifier, out precision)) {
90 return FormatCustom (format1, value, nfi, format);
93 Format formatData = format1;//(precision > format1.dec_len+1) ? format2 : format1;
97 return FormatCurrency (formatData, value, nfi, precision);
99 throw new FormatException(Locale.GetText(
100 "The specified format is invalid") + ": " + format);
102 formatData = (precision > format1.dec_len) ? format2 : format1;
103 return FormatExponential (formatData, value, nfi, precision, format[0]);
105 return FormatFixedPoint (formatData, value, nfi, precision);
107 return FormatGeneral (formatData, value, nfi, precision);
109 return FormatNumber (formatData, value, nfi, precision);
111 return FormatPercent (formatData, value, nfi, precision);
113 return FormatReversible (value, nfi, precision);
115 throw new FormatException(Locale.GetText(
116 "The specified format is invalid") + ": " + format);
118 throw new FormatException(Locale.GetText(
119 "The specified format is invalid") + ": " + format);
123 private bool ParseFormat (string format,
124 out char specifier, out int precision) {
126 precision = format2.dec_len;
128 // FIXME: Math.Round is used and the max is 15.
133 switch (format.Length) {
135 specifier = Char.ToUpperInvariant(format[0]);
139 if (Char.IsLetter(format[0]) && Char.IsDigit(format[1])) {
140 specifier = Char.ToUpperInvariant(format[0]);
141 precision = Convert.ToInt32(format[1] - '0');
146 if (Char.IsLetter(format[0]) && Char.IsDigit(format[1])
147 && Char.IsDigit(format[2])) {
148 specifier = Char.ToUpperInvariant(format[0]);
149 precision = Convert.ToInt32(format.Substring(1, 2));
157 // Math.Round use banker's rounding while this is not what must
158 // be used for string formatting (see bug #60111)
159 // http://bugzilla.ximian.com/show_bug.cgi?id=60111
161 // FIXME: should be moved out of here post Mono 1.0
162 private double Round (double value)
164 double int_part = Math.Floor (value);
\r
165 double dec_part = value - int_part;
\r
166 if (dec_part >= 0.5) {
\r
172 // FIXME: should be moved out of here post Mono 1.0
173 private double Round (double value, int digits)
176 return Round (value);
\r
177 double p = Math.Pow (10, digits);
\r
178 double int_part = Math.Floor (value);
\r
179 double dec_part = value - int_part;
\r
180 dec_part *= 1000000000000000L;
\r
181 dec_part = Math.Floor (dec_part);
\r
182 dec_part /= (1000000000000000L / p);
\r
183 dec_part = Round (dec_part);
\r
185 return int_part + dec_part;
\r
188 private void Normalize (Format formatData, double value, int precision,
189 out long mantissa, out int exponent) {
193 Double.IsInfinity(value) ||
194 Double.IsNaN(value)) {
197 value = Math.Abs(value);
198 if (precision <= (formatData.dec_len) && precision >= 0) {
199 value = Round (value, precision);
203 Double.IsInfinity(value) ||
204 Double.IsNaN(value)) {
208 if (value >= formatData.p10) {
209 while (value >= formatData.p10) {
214 else if (value < formatData.p) {
215 while (value < formatData.p) {
220 mantissa = (long) Round(value);
223 private string FormatCurrency (Format formatData, double value,
224 NumberFormatInfo nfi, int precision) {
226 precision = (precision >= 0) ? precision : nfi.CurrencyDecimalDigits;
227 string numb = FormatNumberInternal (formatData, value, nfi, precision);
229 switch (nfi.CurrencyNegativePattern) {
231 return "(" + nfi.CurrencySymbol + numb + ")";
233 return nfi.NegativeSign + nfi.CurrencySymbol + numb;
235 return nfi.CurrencySymbol + nfi.NegativeSign + numb;
237 return nfi.CurrencySymbol + numb + nfi.NegativeSign;
239 return "(" + numb + nfi.CurrencySymbol + ")";
241 return nfi.NegativeSign + numb + nfi.CurrencySymbol;
243 return numb + nfi.NegativeSign + nfi.CurrencySymbol;
245 return numb + nfi.CurrencySymbol + nfi.NegativeSign;
247 return nfi.NegativeSign + numb + " " + nfi.CurrencySymbol;
249 return nfi.NegativeSign + nfi.CurrencySymbol + " " + numb;
251 return numb + " " + nfi.CurrencySymbol + nfi.NegativeSign;
253 return nfi.CurrencySymbol + " " + numb + nfi.NegativeSign;
255 return nfi.CurrencySymbol + " " + nfi.NegativeSign + numb;
257 return numb + nfi.NegativeSign + " " + nfi.CurrencySymbol;
259 return "(" + nfi.CurrencySymbol + " " + numb + ")";
261 return "(" + numb + " " + nfi.CurrencySymbol + ")";
263 throw new ArgumentException(Locale.GetText(
264 "Invalid CurrencyNegativePattern"));
268 switch (nfi.CurrencyPositivePattern) {
270 return nfi.CurrencySymbol + numb ;
272 return numb + nfi.CurrencySymbol;
274 return nfi.CurrencySymbol + " " + numb;
276 return numb + " " + nfi.CurrencySymbol;
278 throw new ArgumentException(Locale.GetText(
279 "invalid CurrencyPositivePattern"));
284 private string FormatExponential (Format formatData, double value, NumberFormatInfo nfi,
285 int precision, char exp_char) {
286 StringBuilder sb = new StringBuilder();
287 precision = (precision >= 0) ? precision : 6;
288 int decimals = precision;
291 Normalize (formatData, value, precision, out mantissa, out exponent);
292 if (formatData.dec_len > precision) {
293 double aux = mantissa;
294 for (int i = 0; i < formatData.dec_len - precision; i++) {
297 mantissa = (long) Round(aux);
298 for (int i = 0; i < formatData.dec_len - precision; i++) {
302 bool not_null = false;
303 if (mantissa != 0.0) {
304 for (int i = 0; i < formatData.dec_len || mantissa >= 10; i++) {
305 if ((not_null == false) && ((mantissa % 10) != 0)) {
309 sb.Insert(0, (char)('0' + (mantissa % 10)));
317 sb = new StringBuilder();
318 sb.Append((char)('0' + (mantissa % 10)));
321 while (precision > 0) {
325 if (sb.Length == 0) {
328 sb.Insert (0, (char)('0' + (mantissa % 10)) +
329 nfi.NumberDecimalSeparator);
332 sb.Append(exp_char + nfi.PositiveSign);
335 sb.Append(exp_char + nfi.NegativeSign);
337 sb.Append(Math.Abs(exponent).ToString("000"));
339 sb.Insert(0, nfi.NegativeSign);
341 return sb.ToString();
344 private string FormatFixedPoint (Format formatData, double value,
345 NumberFormatInfo nfi, int precision) {
346 StringBuilder sb = new StringBuilder();
347 precision = (precision >= 0) ? precision : nfi.NumberDecimalDigits;
348 int decimals = precision;
351 Normalize (formatData, value, precision, out mantissa, out exponent);
353 while (decimals > 0) {
359 int decimal_limit = -(decimals + 1);
360 while (exponent < 0) {
361 if (exponent > decimal_limit) {
362 sb.Insert(0, (char)('0' + (mantissa % 10)));
369 sb.Append ('0', decimals);
373 if (precision != 0) {
374 sb.Insert(0, nfi.NumberDecimalSeparator);
380 while (exponent > 0) {
384 while (mantissa != 0) {
385 sb.Insert(0, (char)('0' + (mantissa % 10)));
390 sb.Insert(0, nfi.NegativeSign);
392 return sb.ToString();
395 private string FormatGeneral (Format formatData, double value,
396 NumberFormatInfo nfi, int precision) {
397 StringBuilder sb = new StringBuilder();
402 precision = (precision > 0) ?
403 precision : formatData.dec_len+1;
407 Normalize (formatData, value, precision, out mantissa, out exponent);
409 double dmant = mantissa;
410 for (int i = 0; i < formatData.dec_len - precision + 1; i++) {
413 mantissa = (long) Round (dmant);
414 for (int i = 0; i < formatData.dec_len - precision + 1; i++) {
419 /* Calculate the exponent we would get using the scientific notation */
420 int snExponent = exponent;
421 long snMantissa = mantissa;
423 while (snMantissa >= 10) {
428 if (snExponent > -5 && snExponent < precision) {
429 bool not_null = false;
430 while (exponent < 0) {
431 if ((not_null == false) && ((mantissa % 10) != 0)) {
435 sb.Insert(0, (char)('0' + (mantissa % 10)));
440 if (sb.Length != 0) {
441 sb.Insert(0, nfi.NumberDecimalSeparator);
447 while (mantissa > 0) {
448 sb.Insert(0, (char)('0' + (mantissa % 10)));
454 bool not_null = false;
455 while (mantissa >= 10) {
456 if ((not_null == false) && ((mantissa % 10) != 0)) {
460 sb.Insert (0, (char)('0' + (mantissa % 10)));
466 sb.Insert(0, nfi.NumberDecimalSeparator);
468 sb.Insert(0, (char)('0' + (mantissa % 10)) );
471 sb.Append("E" + nfi.PositiveSign);
474 sb.Append("E" + nfi.NegativeSign);
476 sb.Append(Math.Abs(exponent).ToString("00"));
479 sb.Insert(0, nfi.NegativeSign);
482 return sb.ToString();
485 private string FormatNumber (Format formatData, double value, NumberFormatInfo nfi, int precision) {
487 precision = (precision >= 0) ? precision : nfi.NumberDecimalDigits;
488 string numb = FormatNumberInternal (formatData, value, nfi, precision);
490 switch (nfi.NumberNegativePattern) {
492 return "(" + numb + ")";
494 return nfi.NegativeSign + numb;
496 return nfi.NegativeSign + " " + numb;
498 return numb + nfi.NegativeSign;
500 return numb + " " + nfi.NegativeSign;
502 throw new ArgumentException(Locale.GetText(
503 "Invalid NumberNegativePattern"));
509 private string FormatPercent (Format formatData, double value, NumberFormatInfo nfi,
512 precision = (precision >= 0) ? precision : nfi.PercentDecimalDigits;
513 string numb = FormatNumberInternal (formatData, value*100, nfi, precision);
515 switch (nfi.PercentNegativePattern) {
517 return nfi.NegativeSign + numb + " " + nfi.PercentSymbol;
519 return nfi.NegativeSign + numb + nfi.PercentSymbol;
521 return nfi.NegativeSign + nfi.PercentSymbol + numb;
523 throw new ArgumentException(Locale.GetText(
524 "Invalid PercentNegativePattern"));
528 switch (nfi.PercentPositivePattern) {
530 return numb + " " + nfi.PercentSymbol;
532 return numb + nfi.PercentSymbol;
534 return nfi.PercentSymbol + numb;
536 throw new ArgumentException(Locale.GetText(
537 "invalid PercehtPositivePattern"));
542 private string FormatNumberInternal (Format formatData, double value, NumberFormatInfo nfi, int precision)
544 StringBuilder sb = new StringBuilder();
545 int decimals = precision;
548 Normalize (formatData, value, precision, out mantissa, out exponent);
550 while (decimals > 0) {
556 int decimal_limit = -(decimals + 1);
557 while (exponent < 0) {
558 if (exponent > decimal_limit) {
559 sb.Insert(0, (char)('0' + (mantissa % 10)));
566 sb.Append ('0', decimals);
570 if (precision != 0) {
571 sb.Insert(0, nfi.NumberDecimalSeparator);
579 int groupSize = nfi.NumberGroupSizes[0];
580 if (groupSize == 0) groupSize = int.MaxValue;
582 while (exponent > 0 || mantissa != 0) {
584 if (groupPos == groupSize) {
585 sb.Insert (0, nfi.NumberGroupSeparator);
587 if (groupIndex < nfi.NumberGroupSizes.Length - 1) {
589 groupSize = nfi.NumberGroupSizes[groupIndex];
590 if (groupSize == 0) groupSize = int.MaxValue;
599 sb.Insert(0, (char)('0' + (mantissa % 10)));
606 return sb.ToString();
609 // from http://www.yoda.arachsys.com/csharp/floatingpoint.html
610 // used with permission from original author
611 private string FormatReversible (double value, NumberFormatInfo nfi, int precision)
613 // Translate the double into sign, exponent and mantissa.
614 long bits = BitConverter.DoubleToInt64Bits (value);
615 bool negative = (bits >> 63)==1;
616 int exponent = (int) ((bits >> 52) & 0x7ffL);
617 long mantissa = bits & 0xfffffffffffffL;
619 // Subnormal numbers; exponent is effectively one higher,
620 // but there's no extra normalisation bit in the mantissa
624 // Normal numbers; leave exponent as it is but add extra
625 // bit to the front of the mantissa
627 mantissa = mantissa | (1L<<52);
630 // Bias the exponent. It's actually biased by 1023, but we're
631 // treating the mantissa as m.0 rather than 0.m, so we need
632 // to subtract another 52 from it.
640 while((mantissa & 1) == 0) {
641 // i.e., Mantissa is even
646 // Construct a new decimal expansion with the mantissa
647 ArbitraryDecimal ad = new ArbitraryDecimal (mantissa);
649 // If the exponent is less than 0, we need to repeatedly
650 // divide by 2 - which is the equivalent of multiplying
651 // by 5 and dividing by 10.
653 for (int i=0; i < -exponent; i++)
655 ad.Shift (-exponent);
657 // Otherwise, we need to repeatedly multiply by 2
659 for (int i=0; i < exponent; i++)
663 // Finally, return the string with an appropriate sign
665 return nfi.NegativeSign + ad.ToString (nfi);
667 return ad.ToString (nfi);
670 private string FormatCustom (Format formatData, double value,
671 NumberFormatInfo nfi, string format) {
672 int first_semicolon, second_semicolon, third_semicolon;
673 first_semicolon = format.IndexOf(';');
674 second_semicolon = format.IndexOf(';', first_semicolon + 1);
675 if (second_semicolon < 0) {
676 if (first_semicolon == -1) {
678 string result = FormatCustomParser (formatData, value, nfi, format);
682 if (result.Length > 0) {
683 result = nfi.NegativeSign + result;
687 return FormatCustomParser (formatData, value, nfi, format);
691 return FormatCustomParser
692 (formatData, value, nfi, format.Substring(first_semicolon + 1));
694 return FormatCustomParser (formatData, value, nfi,
695 format.Substring(0, first_semicolon - 1));
698 return FormatCustomParser (formatData, value, nfi,
699 format.Substring(0, first_semicolon - 1));
701 else if (value < 0.0) {
702 return FormatCustomParser (formatData, value, nfi,
703 format.Substring (first_semicolon + 1,
704 second_semicolon - first_semicolon - 1));
706 third_semicolon = second_semicolon < 0 ? - 1 : format.IndexOf (';', second_semicolon + 1);
707 if (third_semicolon < 0)
708 return FormatCustomParser (formatData, value,
709 nfi, format.Substring(second_semicolon + 1));
711 return FormatCustomParser (formatData, value,
712 nfi, format.Substring(second_semicolon + 1, third_semicolon - second_semicolon - 1));
715 private struct Flags {
716 public int NumberOfColons;
717 public bool Groupping;
719 public bool Permille;
722 public int FirstFormatPos;
723 public int IntegralLength;
724 public int DecimalLength;
725 public int ExponentialLength;
728 private Flags AnalizeFormat (string format) {
729 Flags f = new Flags();
730 f.NumberOfColons = 0;
735 f.FirstFormatPos = -1;
736 int aux = 0, i = 0, count = 0;
737 foreach (char c in format) {
744 if (f.FirstFormatPos < 0) {
745 f.FirstFormatPos = i;
758 f.IntegralLength = count;
761 f.NumberOfColons = aux;
773 f.DecimalLength = count;
781 f.NumberOfColons = aux;
783 if (f.DecimalLength > 0) {
784 f.ExponentialLength = count;
787 f.DecimalLength = count;
792 private string FormatCustomParser (Format formatData, double value,
793 NumberFormatInfo nfi, string format) {
796 Flags f = AnalizeFormat(format);
797 if (f.FirstFormatPos < 0) {
800 if (((f.Percent) || (f.Permille) || (f.NumberOfColons > 0)) && (f.ExpPos < 0)) {
801 int len = f.DecimalLength;
807 else if (f.Permille) {
811 if (f.NumberOfColons > 0) {
812 len -= (3 * f.NumberOfColons);
813 exp -= 3 * f.NumberOfColons;
818 value = Round(value, len);
819 Normalize (formatData, value, 15, out mantissa, out exponent);
823 value = Round(value, f.DecimalLength);
824 Normalize (formatData, value, 15, out mantissa, out exponent);
826 StringBuilder sb = new StringBuilder();
828 StringBuilder sb_decimal = new StringBuilder();
829 while (mantissa > 0) {
830 sb_decimal.Insert(0, (char)('0' + (mantissa % 10)));
836 for (k = sb_decimal.Length - 1;
837 k >= 0 && sb_decimal[k] == '0'; k--);
838 sb_decimal.Remove(k + 1, sb_decimal.Length - k - 1);
839 for (int i = f.DotPos - 2; i >= 0; i--) {
841 if (i > 0 && format[i-1] == '\\') {
858 sb.Append(sb_decimal[0]);
859 sb.Append(nfi.NumberDecimalSeparator);
860 for (int j = 1, i = f.DotPos + 1; i < f.ExpPos; i++) {
864 sb.Append(format[++i]);
867 if (j >= sb_decimal.Length) {
873 if (j < sb_decimal.Length) {
874 if ((i == f.ExpPos - 1) &&
875 (j < sb_decimal.Length - 1)) {
876 int a = sb_decimal[j] - '0';
877 int b = sb_decimal[j+1] - '0';
878 if (((b == 5) && ((a % 2) == 0)) || (b > 5)) {
881 sb.Append((char)('0' + (a % 10)));
884 sb.Append(sb_decimal[j++]);
893 sb.Append(format[f.ExpPos]);
898 inicio = f.ExpPos + 1;
899 if (format[inicio] == '-') {
902 else if (format[inicio] == '+') {
909 while (fin < format.Length && format[fin++] == '0');
910 StringBuilder sb_exponent = new StringBuilder();
911 exponent = Math.Abs(exponent);
912 while (exponent > 0) {
913 sb_exponent.Insert(0, (char)('0' + (exponent % 10)));
916 while (sb_exponent.Length < (fin - inicio)) {
917 sb_exponent.Insert(0, '0');
919 sb.Append(sb_exponent.ToString());
920 for (int i = fin; i < format.Length; i++) {
921 sb.Append(format[i]);
923 return sb.ToString();
926 f.ExpPos = format.Length;
929 while (exponent < 0) {
930 mantissa = (long) Round((double)mantissa / 10);
933 f.DotPos = format.Length;
936 StringBuilder sb_decimal = new StringBuilder();
937 while (exponent < 0) {
938 sb_decimal.Insert(0, (char)('0' + (mantissa % 10)));
943 for (k = sb_decimal.Length - 1;
944 k >= 0 && sb_decimal[k] == '0'; k--);
945 sb_decimal.Remove(k + 1, sb_decimal.Length - k - 1);
946 if (sb_decimal.Length > 0) {
947 sb.Append(nfi.NumberDecimalSeparator);
949 else if (format[f.DotPos + 1] == '0') {
950 sb.Append(nfi.NumberDecimalSeparator);
952 bool terminado = false;
953 for (int j = 0, i = f.DotPos + 1; i < f.ExpPos; i++) {
954 if (format[i] == '0' || format[i] == '#') {
955 if (j < sb_decimal.Length) {
956 sb.Append(sb_decimal[j++]);
958 else if (format[i] == '0' && !terminado) {
961 else if (format[i] == '#' && !terminado) {
965 else if (format[i] == '\\') {
966 sb.Append(format[++i]);
968 else if (format [i] == '%')
969 sb.Append (nfi.PercentSymbol);
970 else if (format [i] == '\u2030')
971 sb.Append (nfi.PerMilleSymbol);
973 sb.Append(format[i]);
978 for (int i = f.DotPos - 1; i >= f.FirstFormatPos; i--) {
979 if (format[i] == '#' || format[i] == '0') {
980 if (exponent > 0 || mantissa > 0 || format[i] == '0') {
981 if (f.Groupping && gro == nfi.NumberGroupSizes[0]) {
982 sb.Insert(0, nfi.NumberGroupSeparator);
990 else if (mantissa > 0) {
991 sb.Insert(0, (char)('0' + (mantissa % 10)));
994 else if (format[i] == '0') {
999 else if (format [i] == '%')
1000 sb.Insert (0, nfi.PercentSymbol);
1001 else if (format [i] == '\u2030')
1002 sb.Insert (0, nfi.PerMilleSymbol);
1003 else if (format[i] != ',') {
1004 sb.Insert(0, format[i]);
1006 else if (i > 0 && format[i-1] == '\\') {
1007 sb.Insert(0, format[i]);
1011 while (exponent > 0) {
1012 if (f.Groupping && gro == nfi.NumberGroupSizes[0]) {
1013 sb.Insert(0, nfi.NumberGroupSeparator);
1020 while (mantissa > 0) {
1021 if (f.Groupping && gro == nfi.NumberGroupSizes[0]) {
1022 sb.Insert(0, nfi.NumberGroupSeparator);
1026 sb.Insert(0, (char)('0' + (mantissa % 10)));
1029 for (int i = f.FirstFormatPos - 1; i >= 0; i--) {
1030 if (format [i] == '%')
1031 sb.Insert (0, nfi.PercentSymbol);
1032 else if (format [i] == '\u2030')
1033 sb.Insert (0, nfi.PerMilleSymbol);
1034 else if (format [i] != '.')
1035 sb.Insert(0, format[i]);
1037 return sb.ToString();
1042 // from http://www.yoda.arachsys.com/csharp/floatingpoint.html
1043 // used with permission from original author
1044 internal class ArbitraryDecimal {
1045 /// <summary>Digits in the decimal expansion, one byte per digit
1048 /// How many digits are *after* the decimal point
1053 /// Constructs an arbitrary decimal expansion from the given long.
1054 /// The long must not be negative.
1056 internal ArbitraryDecimal (long x)
1058 string tmp = x.ToString (CultureInfo.InvariantCulture);
1059 digits = new byte [tmp.Length];
1060 for (int i=0; i < tmp.Length; i++)
1061 digits[i] = (byte) (tmp[i] - '0');
1066 /// Multiplies the current expansion by the given amount, which should
1069 internal void MultiplyBy (int amount)
1071 byte[] result = new byte [digits.Length+1];
1072 for (int i=digits.Length-1; i >= 0; i--) {
1073 int resultDigit = digits [i] * amount + result [i+1];
1074 result [i] = (byte)(resultDigit / 10);
1075 result [i+1] = (byte)(resultDigit % 10);
1077 if (result [0] != 0) {
1081 Array.Copy (result, 1, digits, 0, digits.Length);
1087 /// Shifts the decimal point; a negative value makes
1088 /// the decimal expansion bigger (as fewer digits come after the
1089 /// decimal place) and a positive value makes the decimal
1090 /// expansion smaller.
1092 internal void Shift (int amount)
1094 decimalPoint += amount;
1098 /// Removes leading/trailing zeroes from the expansion.
1100 internal void Normalize ()
1103 for (first=0; first < digits.Length; first++) {
1104 if (digits [first] != 0)
1109 for (last = digits.Length - 1; last >= 0; last--) {
1110 if (digits [last] != 0)
1114 if ((first == 0) && (last == digits.Length - 1))
1117 byte[] tmp = new byte [last-first+1];
1118 for (int i=0; i < tmp.Length; i++)
1119 tmp [i] = digits [i + first];
1121 decimalPoint -= digits.Length - (last + 1);
1126 /// Converts the value to a proper decimal string representation.
1128 public string ToString (NumberFormatInfo nfi)
1130 char[] digitString = new char [digits.Length];
1131 for (int i=0; i < digits.Length; i++)
1132 digitString [i] = (char)(digits [i] + '0');
1134 // Simplest case - nothing after the decimal point,
1135 // and last real digit is non-zero, eg value=35
1136 if (decimalPoint == 0) {
1137 return new string (digitString);
1140 // Fairly simple case - nothing after the decimal
1141 // point, but some 0s to add, eg value=350
1142 if (decimalPoint < 0) {
1143 return new string (digitString) + new string ('0', -decimalPoint);
1146 // Nothing before the decimal point, eg 0.035
1147 if (decimalPoint >= digitString.Length) {
1148 return "0" + nfi.NumberDecimalSeparator +
1149 new string ('0',(decimalPoint-digitString.Length))+ new string (digitString);
1152 // Most complicated case - part of the string comes
1153 // before the decimal point, part comes after it,
1155 return new string (digitString, 0, digitString.Length - decimalPoint) +
1156 nfi.NumberDecimalSeparator +
1157 new string (digitString, digitString.Length - decimalPoint, decimalPoint);