+2005-01-31 Kazuki Oikawa <kazuki@panicode.com>
+
+ * Patch from Kazuki Oikawa <kazuki@panicode.com> that replaces the
+ old Integer and Floating point number string formatter with a new
+ formatter that is more compliant.
+
2005-01-17 Atsushi Enomoto <atsushi@ximian.com>
* corlib.dll.sources : added CharUnicodeInfo.cs.
+++ /dev/null
-//
-// System.FloatingPointFormatter.cs
-//
-// Authors:
-// Pedro Martinez Julia <yoros@wanadoo.es>
-// Jon Skeet <skeet@pobox.com>
-// Sebastien Pouliot <sebastien@ximian.com>
-//
-// Copyright (C) 2003 Pedro MartÃez Juliá <yoros@wanadoo.es>
-// Copyright (C) 2004 Jon Skeet
-// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
-//
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-//
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-
-using System;
-using System.Text;
-using System.Collections;
-using System.Globalization;
-
-
-namespace System {
-
- internal class FloatingPointFormatter {
-
- struct Format {
- public double p;
- public double p10;
- public int dec_len;
- public int dec_len_min;
- }
-
- Format format1;
- Format format2;
-
- public FloatingPointFormatter
- (double p, double p10, int dec_len, int dec_len_min,
- double p2, double p102, int dec_len2, int dec_len_min2) {
-
- format1.p = p;
- format1.p10 = p10;
- format1.dec_len = dec_len;
- format1.dec_len_min = dec_len_min;
-
- format2.p = p2;
- format2.p10 = p102;
- format2.dec_len = dec_len2;
- format2.dec_len_min = dec_len_min2;
- }
-
- public string GetStringFrom
- (string format, NumberFormatInfo nfi, double value) {
-
- if (format == null || format == "") {
- format = "G";
- }
- if (nfi == null) {
- nfi = NumberFormatInfo.CurrentInfo;
- }
- if (Double.IsNaN(value)) {
- return nfi.NaNSymbol;
- }
- if (Double.IsNegativeInfinity(value)) {
- return nfi.NegativeInfinitySymbol;
- }
- if (Double.IsPositiveInfinity(value)) {
- return nfi.PositiveInfinitySymbol;
- }
-
- char specifier;
- int precision;
- if (!ParseFormat(format, out specifier, out precision)) {
- return FormatCustom (format1, value, nfi, format);
- }
-
- Format formatData = format1;//(precision > format1.dec_len+1) ? format2 : format1;
-
- switch (specifier) {
- case 'C':
- return FormatCurrency (formatData, value, nfi, precision);
- case 'D':
- throw new FormatException(Locale.GetText(
- "The specified format is invalid") + ": " + format);
- case 'E':
- formatData = (precision > format1.dec_len) ? format2 : format1;
- return FormatExponential (formatData, value, nfi, precision, format[0]);
- case 'F':
- return FormatFixedPoint (formatData, value, nfi, precision);
- case 'G':
- return FormatGeneral (formatData, value, nfi, precision);
- case 'N':
- return FormatNumber (formatData, value, nfi, precision);
- case 'P':
- return FormatPercent (formatData, value, nfi, precision);
- case 'R':
- return FormatReversible (value, nfi, precision);
- case 'X':
- throw new FormatException(Locale.GetText(
- "The specified format is invalid") + ": " + format);
- default:
- throw new FormatException(Locale.GetText(
- "The specified format is invalid") + ": " + format);
- }
- }
-
- private bool ParseFormat (string format,
- out char specifier, out int precision) {
- specifier = '\0';
- precision = format2.dec_len;
-
- // FIXME: Math.Round is used and the max is 15.
-
- if (precision > 15)
- precision = 15;
-
- switch (format.Length) {
- case 1:
- specifier = Char.ToUpperInvariant(format[0]);
- precision = -1;
- return true;
- case 2:
- if (Char.IsLetter(format[0]) && Char.IsDigit(format[1])) {
- specifier = Char.ToUpperInvariant(format[0]);
- precision = Convert.ToInt32(format[1] - '0');
- return true;
- }
- break;
- case 3:
- if (Char.IsLetter(format[0]) && Char.IsDigit(format[1])
- && Char.IsDigit(format[2])) {
- specifier = Char.ToUpperInvariant(format[0]);
- precision = Convert.ToInt32(format.Substring(1, 2));
- return true;
- }
- break;
- }
- return false;
- }
-
- // Math.Round use banker's rounding while this is not what must
- // be used for string formatting (see bug #60111)
- // http://bugzilla.ximian.com/show_bug.cgi?id=60111
-
- // FIXME: should be moved out of here post Mono 1.0
- private double Round (double value)
- {
- double int_part = Math.Floor (value);\r
- double dec_part = value - int_part;\r
- if (dec_part >= 0.5) {\r
- int_part++;\r
- }\r
- return int_part;\r
- }
-
- // FIXME: should be moved out of here post Mono 1.0
- private double Round (double value, int digits)
- {
- if (digits == 0)\r
- return Round (value);\r
- double p = Math.Pow (10, digits);\r
- double int_part = Math.Floor (value);\r
- double dec_part = value - int_part;\r
- dec_part *= 1000000000000000L;\r
- dec_part = Math.Floor (dec_part);\r
- dec_part /= (1000000000000000L / p);\r
- dec_part = Round (dec_part);\r
- dec_part /= p;\r
- return int_part + dec_part;\r
- }
-
- private void Normalize (Format formatData, double value, int precision,
- out long mantissa, out int exponent) {
- mantissa = 0;
- exponent = 0;
- if (value == 0.0 ||
- Double.IsInfinity(value) ||
- Double.IsNaN(value)) {
- return;
- }
- value = Math.Abs(value);
- if (precision <= (formatData.dec_len) && precision >= 0) {
- value = Round (value, precision);
- }
-
- if (value == 0.0 ||
- Double.IsInfinity(value) ||
- Double.IsNaN(value)) {
- return;
- }
-
- if (value >= formatData.p10) {
- while (value >= formatData.p10) {
- value /= 10;
- exponent++;
- }
- }
- else if (value < formatData.p) {
- while (value < formatData.p) {
- value *= 10;
- exponent--;
- }
- }
- mantissa = (long) Round(value);
- }
-
- private string FormatCurrency (Format formatData, double value,
- NumberFormatInfo nfi, int precision) {
-
- precision = (precision >= 0) ? precision : nfi.CurrencyDecimalDigits;
- string numb = FormatNumberInternal (formatData, value, nfi, precision);
- if (value < 0) {
- switch (nfi.CurrencyNegativePattern) {
- case 0:
- return "(" + nfi.CurrencySymbol + numb + ")";
- case 1:
- return nfi.NegativeSign + nfi.CurrencySymbol + numb;
- case 2:
- return nfi.CurrencySymbol + nfi.NegativeSign + numb;
- case 3:
- return nfi.CurrencySymbol + numb + nfi.NegativeSign;
- case 4:
- return "(" + numb + nfi.CurrencySymbol + ")";
- case 5:
- return nfi.NegativeSign + numb + nfi.CurrencySymbol;
- case 6:
- return numb + nfi.NegativeSign + nfi.CurrencySymbol;
- case 7:
- return numb + nfi.CurrencySymbol + nfi.NegativeSign;
- case 8:
- return nfi.NegativeSign + numb + " " + nfi.CurrencySymbol;
- case 9:
- return nfi.NegativeSign + nfi.CurrencySymbol + " " + numb;
- case 10:
- return numb + " " + nfi.CurrencySymbol + nfi.NegativeSign;
- case 11:
- return nfi.CurrencySymbol + " " + numb + nfi.NegativeSign;
- case 12:
- return nfi.CurrencySymbol + " " + nfi.NegativeSign + numb;
- case 13:
- return numb + nfi.NegativeSign + " " + nfi.CurrencySymbol;
- case 14:
- return "(" + nfi.CurrencySymbol + " " + numb + ")";
- case 15:
- return "(" + numb + " " + nfi.CurrencySymbol + ")";
- default:
- throw new ArgumentException(Locale.GetText(
- "Invalid CurrencyNegativePattern"));
- }
- }
- else {
- switch (nfi.CurrencyPositivePattern) {
- case 0:
- return nfi.CurrencySymbol + numb ;
- case 1:
- return numb + nfi.CurrencySymbol;
- case 2:
- return nfi.CurrencySymbol + " " + numb;
- case 3:
- return numb + " " + nfi.CurrencySymbol;
- default:
- throw new ArgumentException(Locale.GetText(
- "invalid CurrencyPositivePattern"));
- }
- }
- }
-
- private string FormatExponential (Format formatData, double value, NumberFormatInfo nfi,
- int precision, char exp_char) {
- StringBuilder sb = new StringBuilder();
- precision = (precision >= 0) ? precision : 6;
- int decimals = precision;
- long mantissa;
- int exponent;
- Normalize (formatData, value, precision, out mantissa, out exponent);
- if (formatData.dec_len > precision) {
- double aux = mantissa;
- for (int i = 0; i < formatData.dec_len - precision; i++) {
- aux /= 10;
- }
- mantissa = (long) Round(aux);
- for (int i = 0; i < formatData.dec_len - precision; i++) {
- mantissa *= 10;
- }
- }
- bool not_null = false;
- if (mantissa != 0.0) {
- for (int i = 0; i < formatData.dec_len || mantissa >= 10; i++) {
- if ((not_null == false) && ((mantissa % 10) != 0)) {
- not_null = true;
- }
- if (not_null) {
- sb.Insert(0, (char)('0' + (mantissa % 10)));
- precision--;
- }
- mantissa /= 10;
- exponent++;
- }
- }
- if (decimals == 0) {
- sb = new StringBuilder();
- sb.Append((char)('0' + (mantissa % 10)));
- }
- else {
- while (precision > 0) {
- sb.Append('0');
- precision--;
- }
- if (sb.Length == 0) {
- sb.Insert(0, "0");
- }
- sb.Insert (0, (char)('0' + (mantissa % 10)) +
- nfi.NumberDecimalSeparator);
- }
- if (exponent >= 0) {
- sb.Append(exp_char + nfi.PositiveSign);
- }
- else {
- sb.Append(exp_char + nfi.NegativeSign);
- }
- sb.Append(Math.Abs(exponent).ToString("000"));
- if (value < 0.0) {
- sb.Insert(0, nfi.NegativeSign);
- }
- return sb.ToString();
- }
-
- private string FormatFixedPoint (Format formatData, double value,
- NumberFormatInfo nfi, int precision) {
- StringBuilder sb = new StringBuilder();
- precision = (precision >= 0) ? precision : nfi.NumberDecimalDigits;
- int decimals = precision;
- long mantissa;
- int exponent;
- Normalize (formatData, value, precision, out mantissa, out exponent);
- if (exponent >= 0) {
- while (decimals > 0) {
- sb.Append("0");
- decimals--;
- }
- }
- else {
- int decimal_limit = -(decimals + 1);
- while (exponent < 0) {
- if (exponent > decimal_limit) {
- sb.Insert(0, (char)('0' + (mantissa % 10)));
- }
- mantissa /= 10;
- exponent++;
- decimals--;
- }
- if (decimals > 0) {
- sb.Append ('0', decimals);
- decimals = 0;
- }
- }
- if (precision != 0) {
- sb.Insert(0, nfi.NumberDecimalSeparator);
- }
- if (mantissa == 0) {
- sb.Insert(0, "0");
- }
- else {
- while (exponent > 0) {
- sb.Insert(0, "0");
- exponent--;
- }
- while (mantissa != 0) {
- sb.Insert(0, (char)('0' + (mantissa % 10)));
- mantissa /= 10;
- }
- }
- if (value < 0.0) {
- sb.Insert(0, nfi.NegativeSign);
- }
- return sb.ToString();
- }
-
- private string FormatGeneral (Format formatData, double value,
- NumberFormatInfo nfi, int precision) {
- StringBuilder sb = new StringBuilder();
- if (value == 0.0) {
- sb.Append("0");
- }
- else {
- precision = (precision > 0) ?
- precision : formatData.dec_len+1;
-
- long mantissa;
- int exponent;
- Normalize (formatData, value, precision, out mantissa, out exponent);
- if (precision > 0) {
- double dmant = mantissa;
- for (int i = 0; i < formatData.dec_len - precision + 1; i++) {
- dmant /= 10;
- }
- mantissa = (long) Round (dmant);
- for (int i = 0; i < formatData.dec_len - precision + 1; i++) {
- mantissa *= 10;
- }
- }
-
- /* Calculate the exponent we would get using the scientific notation */
- int snExponent = exponent;
- long snMantissa = mantissa;
-
- while (snMantissa >= 10) {
- snMantissa /= 10;
- snExponent++;
- }
-
- if (snExponent > -5 && snExponent < precision) {
- bool not_null = false;
- while (exponent < 0) {
- if ((not_null == false) && ((mantissa % 10) != 0)) {
- not_null = true;
- }
- if (not_null) {
- sb.Insert(0, (char)('0' + (mantissa % 10)));
- }
- mantissa /= 10;
- exponent++;
- }
- if (sb.Length != 0) {
- sb.Insert(0, nfi.NumberDecimalSeparator);
- }
- if (mantissa == 0) {
- sb.Insert(0, "0");
- }
- else {
- while (mantissa > 0) {
- sb.Insert(0, (char)('0' + (mantissa % 10)));
- mantissa /= 10;
- }
- }
- }
- else {
- bool not_null = false;
- while (mantissa >= 10) {
- if ((not_null == false) && ((mantissa % 10) != 0)) {
- not_null = true;
- }
- if (not_null) {
- sb.Insert (0, (char)('0' + (mantissa % 10)));
- }
- mantissa /= 10;
- exponent++;
- }
- if (sb.Length != 0)
- sb.Insert(0, nfi.NumberDecimalSeparator);
-
- sb.Insert(0, (char)('0' + (mantissa % 10)) );
-
- if (exponent > 0) {
- sb.Append("E" + nfi.PositiveSign);
- }
- else {
- sb.Append("E" + nfi.NegativeSign);
- }
- sb.Append(Math.Abs(exponent).ToString("00"));
- }
- if (value < 0.0) {
- sb.Insert(0, nfi.NegativeSign);
- }
- }
- return sb.ToString();
- }
-
- private string FormatNumber (Format formatData, double value, NumberFormatInfo nfi, int precision) {
-
- precision = (precision >= 0) ? precision : nfi.NumberDecimalDigits;
- string numb = FormatNumberInternal (formatData, value, nfi, precision);
- if (value < 0) {
- switch (nfi.NumberNegativePattern) {
- case 0:
- return "(" + numb + ")";
- case 1:
- return nfi.NegativeSign + numb;
- case 2:
- return nfi.NegativeSign + " " + numb;
- case 3:
- return numb + nfi.NegativeSign;
- case 4:
- return numb + " " + nfi.NegativeSign;
- default:
- throw new ArgumentException(Locale.GetText(
- "Invalid NumberNegativePattern"));
- }
- }
- return numb;
- }
-
- private string FormatPercent (Format formatData, double value, NumberFormatInfo nfi,
- int precision) {
-
- precision = (precision >= 0) ? precision : nfi.PercentDecimalDigits;
- string numb = FormatNumberInternal (formatData, value*100, nfi, precision);
- if (value < 0) {
- switch (nfi.PercentNegativePattern) {
- case 0:
- return nfi.NegativeSign + numb + " " + nfi.PercentSymbol;
- case 1:
- return nfi.NegativeSign + numb + nfi.PercentSymbol;
- case 2:
- return nfi.NegativeSign + nfi.PercentSymbol + numb;
- default:
- throw new ArgumentException(Locale.GetText(
- "Invalid PercentNegativePattern"));
- }
- }
- else {
- switch (nfi.PercentPositivePattern) {
- case 0:
- return numb + " " + nfi.PercentSymbol;
- case 1:
- return numb + nfi.PercentSymbol;
- case 2:
- return nfi.PercentSymbol + numb;
- default:
- throw new ArgumentException(Locale.GetText(
- "invalid PercehtPositivePattern"));
- }
- }
- }
-
- private string FormatNumberInternal (Format formatData, double value, NumberFormatInfo nfi, int precision)
- {
- StringBuilder sb = new StringBuilder();
- int decimals = precision;
- long mantissa;
- int exponent;
- Normalize (formatData, value, precision, out mantissa, out exponent);
- if (exponent >= 0) {
- while (decimals > 0) {
- sb.Append("0");
- decimals--;
- }
- }
- else {
- int decimal_limit = -(decimals + 1);
- while (exponent < 0) {
- if (exponent > decimal_limit) {
- sb.Insert(0, (char)('0' + (mantissa % 10)));
- }
- mantissa /= 10;
- exponent++;
- decimals--;
- }
- if (decimals > 0) {
- sb.Append ('0', decimals);
- decimals = 0;
- }
- }
- if (precision != 0) {
- sb.Insert(0, nfi.NumberDecimalSeparator);
- }
- if (mantissa == 0) {
- sb.Insert(0, "0");
- }
- else {
- int groupIndex = 0;
- int groupPos = 0;
- int groupSize = nfi.NumberGroupSizes[0];
- if (groupSize == 0) groupSize = int.MaxValue;
-
- while (exponent > 0 || mantissa != 0) {
-
- if (groupPos == groupSize) {
- sb.Insert (0, nfi.NumberGroupSeparator);
- groupPos = 0;
- if (groupIndex < nfi.NumberGroupSizes.Length - 1) {
- groupIndex++;
- groupSize = nfi.NumberGroupSizes[groupIndex];
- if (groupSize == 0) groupSize = int.MaxValue;
- }
- }
-
- if (exponent > 0) {
- sb.Insert (0, "0");
- exponent--;
- }
- else {
- sb.Insert(0, (char)('0' + (mantissa % 10)));
- mantissa /= 10;
- }
-
- groupPos++;
- }
- }
- return sb.ToString();
- }
-
- // from http://www.yoda.arachsys.com/csharp/floatingpoint.html
- // used with permission from original author
- private string FormatReversible (double value, NumberFormatInfo nfi, int precision)
- {
- // Translate the double into sign, exponent and mantissa.
- long bits = BitConverter.DoubleToInt64Bits (value);
- bool negative = ((bits >> 63) == -1);
- int exponent = (int) ((bits >> 52) & 0x7ffL);
- long mantissa = bits & 0xfffffffffffffL;
-
- // Subnormal numbers; exponent is effectively one higher,
- // but there's no extra normalisation bit in the mantissa
- if (exponent == 0) {
- exponent++;
- }
- // Normal numbers; leave exponent as it is but add extra
- // bit to the front of the mantissa
- else {
- mantissa = mantissa | (1L<<52);
- }
-
- // Bias the exponent. It's actually biased by 1023, but we're
- // treating the mantissa as m.0 rather than 0.m, so we need
- // to subtract another 52 from it.
- exponent -= 1075;
-
- if (mantissa == 0) {
- return "0";
- }
-
- // Normalize
- while((mantissa & 1) == 0) {
- // i.e., Mantissa is even
- mantissa >>= 1;
- exponent++;
- }
-
- // Construct a new decimal expansion with the mantissa
- ArbitraryDecimal ad = new ArbitraryDecimal (mantissa);
-
- // If the exponent is less than 0, we need to repeatedly
- // divide by 2 - which is the equivalent of multiplying
- // by 5 and dividing by 10.
- if (exponent < 0) {
- for (int i=0; i < -exponent; i++)
- ad.MultiplyBy (5);
- ad.Shift (-exponent);
- }
- // Otherwise, we need to repeatedly multiply by 2
- else {
- for (int i=0; i < exponent; i++)
- ad.MultiplyBy(2);
- }
-
- // Finally, return the string with an appropriate sign
- if (negative)
- return nfi.NegativeSign + ad.ToString (nfi);
- else
- return ad.ToString (nfi);
- }
-
- private string FormatCustom (Format formatData, double value,
- NumberFormatInfo nfi, string format) {
- int first_semicolon, second_semicolon, third_semicolon;
- first_semicolon = format.IndexOf(';');
- second_semicolon = format.IndexOf(';', first_semicolon + 1);
- if (second_semicolon < 0) {
- if (first_semicolon == -1) {
- if (value < 0.0) {
- string result = FormatCustomParser (formatData, value, nfi, format);
- if (result == "0") {
- return "0";
- }
- if (result.Length > 0) {
- result = nfi.NegativeSign + result;
- }
- return result;
- }
- return FormatCustomParser (formatData, value, nfi, format);
-
- }
- if (value < 0.0) {
- return FormatCustomParser
- (formatData, value, nfi, format.Substring(first_semicolon + 1));
- }
- return FormatCustomParser (formatData, value, nfi,
- format.Substring(0, first_semicolon - 1));
- }
- if (value > 0.0) {
- return FormatCustomParser (formatData, value, nfi,
- format.Substring(0, first_semicolon - 1));
- }
- else if (value < 0.0) {
- return FormatCustomParser (formatData, value, nfi,
- format.Substring (first_semicolon + 1,
- second_semicolon - first_semicolon - 1));
- }
- third_semicolon = second_semicolon < 0 ? - 1 : format.IndexOf (';', second_semicolon + 1);
- if (third_semicolon < 0)
- return FormatCustomParser (formatData, value,
- nfi, format.Substring(second_semicolon + 1));
- else
- return FormatCustomParser (formatData, value,
- nfi, format.Substring(second_semicolon + 1, third_semicolon - second_semicolon - 1));
- }
-
- private struct Flags {
- public int NumberOfColons;
- public bool Groupping;
- public bool Percent;
- public bool Permille;
- public int DotPos;
- public int ExpPos;
- public int FirstFormatPos;
- public int IntegralLength;
- public int DecimalLength;
- public int ExponentialLength;
- }
-
- private Flags AnalizeFormat (string format) {
- Flags f = new Flags();
- f.NumberOfColons = 0;
- f.DotPos = -1;
- f.ExpPos = -1;
- f.Groupping = false;
- f.Percent = false;
- f.FirstFormatPos = -1;
- int aux = 0, i = 0, count = 0;
- bool inQuote = false;
- foreach (char c in format) {
- if (c == '\'') {
- if (inQuote)
- inQuote = false;
- else
- inQuote = true;
- i++;
- continue;
- } else if (inQuote) {
- i++;
- continue;
- }
-
- switch (c) {
- case ',':
- aux++;
- break;
- case '0':
- case '#':
- if (f.FirstFormatPos < 0) {
- f.FirstFormatPos = i;
- }
- if (aux > 0) {
- f.Groupping = true;
- aux = 0;
- }
- if (count < 15)
- count++;
- break;
- case '.':
- if (f.DotPos >= 0)
- break; // ignore
- f.DotPos = i;
- f.IntegralLength = count;
- count = 0;
- if (aux > 0) {
- f.NumberOfColons = aux;
- aux = 0;
- }
- break;
- case '%':
- f.Percent = true;
- break;
- case '\u2030':
- f.Permille = true;
- break;
- case 'e':
- case 'E':
- f.DecimalLength = count;
- count = 0;
- f.ExpPos = i;
- break;
- }
- i++;
- }
- if (inQuote)
- throw new FormatException ("Literal in format string is not correctly terminated.");
- if (aux > 0) {
- f.NumberOfColons = aux;
- }
- if (f.DecimalLength > 0) {
- f.ExponentialLength = count;
- }
- else {
- f.DecimalLength = count;
- }
- return f;
- }
-
- private string FormatCustomParser (Format formatData, double value,
- NumberFormatInfo nfi, string format) {
- long mantissa;
- int exponent;
- Flags f = AnalizeFormat(format);
- if (f.FirstFormatPos < 0) {
- return format;
- }
- if (((f.Percent) || (f.Permille) || (f.NumberOfColons > 0)) && (f.ExpPos < 0)) {
- int len = f.DecimalLength;
- int exp = 0;
- if (f.Percent) {
- len += 2;
- exp += 2;
- }
- else if (f.Permille) {
- len += 3;
- exp += 3;
- }
- if (f.NumberOfColons > 0) {
- len -= (3 * f.NumberOfColons);
- exp -= 3 * f.NumberOfColons;
- }
- if (len < 0) {
- len = 0;
- }
- value = Round(value, len);
- Normalize (formatData, value, 15, out mantissa, out exponent);
- exponent += exp;
- }
- else {
- value = Round(value, f.DecimalLength);
- Normalize (formatData, value, 15, out mantissa, out exponent);
- }
- StringBuilder sb = new StringBuilder();
- if (f.ExpPos > 0) {
- StringBuilder sb_decimal = new StringBuilder();
- while (mantissa > 0) {
- sb_decimal.Insert(0, (char)('0' + (mantissa % 10)));
- mantissa /= 10;
- exponent++;
- }
- exponent--;
- int k;
- for (k = sb_decimal.Length - 1;
- k >= 0 && sb_decimal[k] == '0'; k--);
- sb_decimal.Remove(k + 1, sb_decimal.Length - k - 1);
- for (int i = f.DotPos - 2; i >= 0; i--) {
- char c = format[i];
- if (i > 0 && format[i-1] == '\\') {
- sb.Insert(0, c);
- i -= 2;
- continue;
- }
- switch (c) {
- case ',':
- case '#':
- break;
- case '0':
- sb.Insert(0, '0');
- break;
- default:
- sb.Insert(0, c);
- break;
- }
- }
- sb.Append(sb_decimal[0]);
- sb.Append(nfi.NumberDecimalSeparator);
- for (int j = 1, i = f.DotPos + 1; i < f.ExpPos; i++) {
- char c = format[i];
- switch (c) {
- case '\\':
- sb.Append(format[++i]);
- break;
- case '0':
- if (j >= sb_decimal.Length) {
- sb.Append('0');
- break;
- }
- goto case '#';
- case '#':
- if (j < sb_decimal.Length) {
- if ((i == f.ExpPos - 1) &&
- (j < sb_decimal.Length - 1)) {
- int a = sb_decimal[j] - '0';
- int b = sb_decimal[j+1] - '0';
- if (((b == 5) && ((a % 2) == 0)) || (b > 5)) {
- a++;
- }
- sb.Append((char)('0' + (a % 10)));
- }
- else {
- sb.Append(sb_decimal[j++]);
- }
- }
- break;
- default:
- sb.Append(c);
- break;
- }
- }
- sb.Append(format[f.ExpPos]);
- if (exponent < 0) {
- sb.Append('-');
- }
- int fin, inicio;
- inicio = f.ExpPos + 1;
- if (format[inicio] == '-') {
- inicio++;
- }
- else if (format[inicio] == '+') {
- if (exponent >= 0) {
- sb.Append('+');
- }
- inicio++;
- }
- fin = inicio;
- while (fin < format.Length && format[fin++] == '0');
- StringBuilder sb_exponent = new StringBuilder();
- exponent = Math.Abs(exponent);
- while (exponent > 0) {
- sb_exponent.Insert(0, (char)('0' + (exponent % 10)));
- exponent /= 10;
- }
- while (sb_exponent.Length < (fin - inicio)) {
- sb_exponent.Insert(0, '0');
- }
- sb.Append(sb_exponent.ToString());
- for (int i = fin; i < format.Length; i++) {
- sb.Append(format[i]);
- }
- return sb.ToString();
- }
- else {
- f.ExpPos = format.Length;
- }
- if (f.DotPos < 0) {
- while (exponent < 0) {
- mantissa = (long) Round((double)mantissa / 10);
- exponent++;
- }
- f.DotPos = format.Length;
- }
- else {
- StringBuilder sb_decimal = new StringBuilder();
- while (exponent < 0) {
- sb_decimal.Insert(0, (char)('0' + (mantissa % 10)));
- mantissa /= 10;
- exponent++;
- }
- int k;
- for (k = sb_decimal.Length - 1;
- k >= 0 && sb_decimal[k] == '0'; k--);
- sb_decimal.Remove(k + 1, sb_decimal.Length - k - 1);
- if (sb_decimal.Length > 0) {
- sb.Append(nfi.NumberDecimalSeparator);
- }
- else if (format[f.DotPos + 1] == '0') {
- sb.Append(nfi.NumberDecimalSeparator);
- }
- bool terminado = false;
- for (int j = 0, i = f.DotPos + 1; i < f.ExpPos; i++) {
- if (format[i] == '0' || format[i] == '#') {
- if (j < sb_decimal.Length) {
- sb.Append(sb_decimal[j++]);
- }
- else if (format[i] == '0' && !terminado) {
- sb.Append('0');
- }
- else if (format[i] == '#' && !terminado) {
- terminado = true;
- }
- }
- else if (format[i] == '\\') {
- sb.Append(format[++i]);
- }
- else if (format [i] == '%')
- sb.Append (nfi.PercentSymbol);
- else if (format [i] == '\u2030')
- sb.Append (nfi.PerMilleSymbol);
- else if (format [i] == '\'') {
- int l = ++i;
- while (i < format.Length) {
- if (format [i] == '\'')
- break;
- i++;
- }
- sb.Insert (0, format.Substring (l, i - l));
- }
- else {
- sb.Append(format[i]);
- }
- }
- }
- int gro = 0;
- for (int i = f.DotPos - 1; i >= f.FirstFormatPos; i--) {
- if (format[i] == '#' || format[i] == '0') {
- if (exponent > 0 || mantissa > 0 || format[i] == '0') {
- if (f.Groupping && gro == nfi.NumberGroupSizes[0]) {
- sb.Insert(0, nfi.NumberGroupSeparator);
- gro = 0;
- }
- gro++;
- if (exponent > 0) {
- sb.Insert(0, '0');
- exponent--;
- }
- else if (mantissa > 0) {
- sb.Insert(0, (char)('0' + (mantissa % 10)));
- mantissa /= 10;
- }
- else if (format[i] == '0') {
- sb.Insert(0, '0');
- }
- }
- }
- else if (format [i] == '%')
- sb.Insert (0, nfi.PercentSymbol);
- else if (format [i] == '\u2030')
- sb.Insert (0, nfi.PerMilleSymbol);
- else if (format [i] == '\'') {
- int l = i;
- while (i >= 0) {
- if (format [i] == '\'')
- break;
- i--;
- }
- sb.Insert (0, format.Substring (i, l - i));
- }
- else if (format[i] != ',') {
- sb.Insert(0, format[i]);
- }
- else if (i > 0 && format[i-1] == '\\') {
- sb.Insert(0, format[i]);
- i -= 2;
- }
- }
- while (exponent > 0) {
- if (f.Groupping && gro == nfi.NumberGroupSizes[0]) {
- sb.Insert(0, nfi.NumberGroupSeparator);
- gro = 0;
- }
- gro++;
- sb.Insert(0, '0');
- exponent--;
- }
- while (mantissa > 0) {
- if (f.Groupping && gro == nfi.NumberGroupSizes[0]) {
- sb.Insert(0, nfi.NumberGroupSeparator);
- gro = 0;
- }
- gro++;
- sb.Insert(0, (char)('0' + (mantissa % 10)));
- mantissa /= 10;
- }
- for (int i = f.FirstFormatPos - 1; i >= 0; i--) {
- if (format [i] == '%')
- sb.Insert (0, nfi.PercentSymbol);
- else if (format [i] == '\u2030')
- sb.Insert (0, nfi.PerMilleSymbol);
- else if (format [i] == '\'') {
- int l = i;
- while (i >= 0) {
- if (format [i] == '\'')
- break;
- i--;
- }
- sb.Insert (0, format.Substring (i, l - i));
- }
- else if (format [i] != '.')
- sb.Insert(0, format[i]);
- }
- return sb.ToString();
- }
-
- }
-
- // from http://www.yoda.arachsys.com/csharp/floatingpoint.html
- // used with permission from original author
- internal class ArbitraryDecimal {
- /// <summary>Digits in the decimal expansion, one byte per digit
- byte[] digits;
- /// <summary>
- /// How many digits are *after* the decimal point
- /// </summary>
- int decimalPoint=0;
-
- /// <summary>
- /// Constructs an arbitrary decimal expansion from the given long.
- /// The long must not be negative.
- /// </summary>
- internal ArbitraryDecimal (long x)
- {
- string tmp = x.ToString (CultureInfo.InvariantCulture);
- digits = new byte [tmp.Length];
- for (int i=0; i < tmp.Length; i++)
- digits[i] = (byte) (tmp[i] - '0');
- Normalize ();
- }
-
- /// <summary>
- /// Multiplies the current expansion by the given amount, which should
- /// only be 2 or 5.
- /// </summary>
- internal void MultiplyBy (int amount)
- {
- byte[] result = new byte [digits.Length+1];
- for (int i=digits.Length-1; i >= 0; i--) {
- int resultDigit = digits [i] * amount + result [i+1];
- result [i] = (byte)(resultDigit / 10);
- result [i+1] = (byte)(resultDigit % 10);
- }
- if (result [0] != 0) {
- digits = result;
- }
- else {
- Array.Copy (result, 1, digits, 0, digits.Length);
- }
- Normalize ();
- }
-
- /// <summary>
- /// Shifts the decimal point; a negative value makes
- /// the decimal expansion bigger (as fewer digits come after the
- /// decimal place) and a positive value makes the decimal
- /// expansion smaller.
- /// </summary>
- internal void Shift (int amount)
- {
- decimalPoint += amount;
- }
-
- /// <summary>
- /// Removes leading/trailing zeroes from the expansion.
- /// </summary>
- internal void Normalize ()
- {
- int first;
- for (first=0; first < digits.Length; first++) {
- if (digits [first] != 0)
- break;
- }
-
- int last;
- for (last = digits.Length - 1; last >= 0; last--) {
- if (digits [last] != 0)
- break;
- }
-
- if ((first == 0) && (last == digits.Length - 1))
- return;
-
- byte[] tmp = new byte [last-first+1];
- for (int i=0; i < tmp.Length; i++)
- tmp [i] = digits [i + first];
-
- decimalPoint -= digits.Length - (last + 1);
- digits = tmp;
- }
-
- /// <summary>
- /// Converts the value to a proper decimal string representation.
- /// </summary>
- public string ToString (NumberFormatInfo nfi)
- {
- char[] digitString = new char [digits.Length];
- for (int i=0; i < digits.Length; i++)
- digitString [i] = (char)(digits [i] + '0');
-
- // Simplest case - nothing after the decimal point,
- // and last real digit is non-zero, eg value=35
- if (decimalPoint == 0) {
- return new string (digitString);
- }
-
- // Fairly simple case - nothing after the decimal
- // point, but some 0s to add, eg value=350
- if (decimalPoint < 0) {
- return new string (digitString) + new string ('0', -decimalPoint);
- }
-
- // Nothing before the decimal point, eg 0.035
- if (decimalPoint >= digitString.Length) {
- return "0" + nfi.NumberDecimalSeparator +
- new string ('0',(decimalPoint-digitString.Length))+ new string (digitString);
- }
-
- // Most complicated case - part of the string comes
- // before the decimal point, part comes after it,
- // eg 3.5
- return new string (digitString, 0, digitString.Length - decimalPoint) +
- nfi.NumberDecimalSeparator +
- new string (digitString, digitString.Length - decimalPoint, decimalPoint);
- }
- }
-}
+++ /dev/null
-//
-// System.IntegerFormatter.cs
-//
-// Author:
-// Derek Holden (dholden@draper.com)
-//
-// (C) Derek Holden dholden@draper.com
-//
-
-//
-// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
-//
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-//
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-
-//
-// Format integer types. Completely based off ECMA docs
-// for IFormattable specification. Has been tested w/
-// all integral types, from boundry to boundry, w/ all
-// formats A## ("G", "G0" ... "G99", "P", "P0" ... "P99").
-//
-// If you make any changes, please make sure to check the
-// boundry format precisions (0, 99) and the min / max values
-// of the data types (Int32.[Max/Min]Value).
-//
-// Using int as an example, it is currently set up as
-//
-// Int32 {
-// int value;
-// public string ToString (string format, NumberFormatInfo nfi) {
-// return IntegerFormatter.NumberToString (format, nfi, value);
-// }
-//
-// IntegerFormatter {
-// public string NumberToString (string format, NumberFormatInfo nfi, int value) {
-// ParseFormat (format);
-// switch (format type) {
-// case 'G' FormatGeneral(value, precision);
-// case 'R' throw Exception("Invalid blah blah");
-// case 'C' FromatCurrency(value, precision, nfi);
-// etc...
-// }
-// }
-// }
-//
-// There is a property in NumberFormatInfo for NegativeSign, though the
-// definition of IFormattable just uses '-' in context. So all the
-// hardcoded uses of '-' in here may need to be changed to nfi.NegativeSign
-//
-// For every integral type.
-//
-// Before every Format<Format Type> block there is a small paragraph
-// detailing its requirements, and a blurb of what I was thinking
-// at the time.
-//
-// Some speedup suggestions to be done when after this appears
-// to be working properly:
-//
-// * Deal w/ out of range numbers better. Specifically with
-// regards to boundry cases such as Long.MinValue etc.
-// The previous way of if (value < 0) value = -value;
-// fails under this assumption, since the largest
-// possible MaxValue is < absolute value of the MinValue.
-// I do the first iteration outside of the loop, and then
-// convert the number to positive, then continue in the loop.
-//
-// * Replace all occurances of max<Type>Length with their
-// numerical values. Plus the places where things are set
-// to max<Type>Length - 1. Hardcode these to numbers.
-//
-// * Move the code for all the NumberToString()'s into the
-// the main ToString (string, NumberFormatInfo) method in
-// the data types themselves. That way they'd be throwing
-// their own exceptions on error and it'd save a function
-// call.
-//
-// * For integer to char buffer transformation, you could
-// implement the calculations of the 10's and 100's place
-// the same time w/ another table to shorten loop time.
-//
-// * Someone smarter can prolly find a much more efficient
-// way of formatting the exponential notation. It's still
-// done in pass, just may have too many repositioning
-// calculations.
-//
-// * Decide whether it be better to have functions that
-// handle formatting for all types, or just cast their
-// values out and format them. Just if library size is
-// more important than speed in saving a cast and a
-// function call.
-//
-
-using System;
-using System.Collections;
-using System.Globalization;
-
-namespace System {
-
- class IntegerFormatter {
-
- const int maxByteLength = 4;
- const int maxShortLength = 6;
- const int maxIntLength = 12;
- const int maxLongLength = 22;
-
- static readonly char [] digitLowerTable =
- { '0', '1', '2', '3', '4', '5', '6', '7',
- '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
-
- static readonly char [] digitUpperTable =
- { '0', '1', '2', '3', '4', '5', '6', '7',
- '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
-
- static bool ParseFormat (string format, out char specifier, out int precision, out bool custom)
- {
- precision = -1;
- specifier = '\0';
- custom = false;
-
- int length = format.Length;
- // TODO: Could an empty string be a custom format string?
- if (length < 1)
- return false;
-
- specifier = format [0];
-
- if (Char.IsLetter (specifier)) {
- switch (length) {
- case 1:
- return true;
- case 2:
- if (Char.IsDigit (format [1])) {
- precision = format [1] - '0';
- return true;
- }
- break;
- case 3:
- if (Char.IsDigit (format [1]) && Char.IsDigit (format [2])) {
- precision = ((format [1] - '0') * 10) + format[2] - '0';
- return true;
- }
- break;
- }
-
- }
-
- // We've got a custom format string.
- custom = true;
- return true;
- }
-
- // ============ Public Interface to all the integer types ============ //
-
- public static string NumberToString (string format, NumberFormatInfo nfi, short value)
- {
- return NumberToString (format, nfi, maxIntLength, 2, (long)value);
- }
-
- public static string NumberToString (string format, NumberFormatInfo nfi, int value)
- {
- return NumberToString (format, nfi, maxIntLength, 4, (long)value);
- }
-
- public static string NumberToString (string format, NumberFormatInfo nfi, long value)
- {
- return NumberToString (format, nfi, maxLongLength, 8, value);
- }
-
- public static string NumberToString (string format, NumberFormatInfo nfi, sbyte value)
- {
- return NumberToString (format, nfi, maxIntLength, 1, (long)value);
- }
-
- public static string NumberToString (string format, NumberFormatInfo nfi, int maxLength, int integerSize, long value)
- {
- char specifier;
- int precision;
- bool custom;
-
- if (!ParseFormat (format, out specifier, out precision, out custom))
- throw new FormatException (Locale.GetText ("The specified format '" + format + "' is invalid"));
-
- if (custom){
- return FormatCustom (format, value, nfi, maxLength);
- }
-
- switch(specifier) {
- case 'c': return FormatCurrency (value, precision, nfi, maxLength);
- case 'C': return FormatCurrency (value, precision, nfi, maxLength);
- case 'd': return FormatDecimal (value, precision, maxLength);
- case 'D': return FormatDecimal (value, precision, maxLength);
- case 'e': return FormatExponential (value, precision, false, maxLength);
- case 'E': return FormatExponential (value, precision, true, maxLength);
- case 'f': return FormatFixedPoint (value, precision, nfi, maxLength);
- case 'F': return FormatFixedPoint (value, precision, nfi, maxLength);
- case 'g': return FormatGeneral (value, precision, nfi, false, maxLength);
- case 'G': return FormatGeneral (value, precision, nfi, true, maxLength);
- case 'n': return FormatNumber (value, precision, nfi, maxLength);
- case 'N': return FormatNumber (value, precision, nfi, maxLength);
- case 'p': return FormatPercent (value, precision, nfi, maxLength);
- case 'P': return FormatPercent (value, precision, nfi, maxLength);
- case 'r': throw new FormatException (Locale.GetText ("The specified format cannot be used in this instance"));
- case 'R': throw new FormatException (Locale.GetText ("The specified format cannot be used in this instance"));
- case 'x': return FormatHexadecimal (value, precision, false, maxLength, integerSize);
- case 'X': return FormatHexadecimal (value, precision, true, maxLength, integerSize);
- default:
- throw new FormatException (Locale.GetText ("The specified format '" + format + "' is invalid"));
- }
- }
-
- public static string NumberToString (string format, NumberFormatInfo nfi, byte value)
- {
- return NumberToString (format, nfi, maxIntLength, 1, (ulong)value);
- }
-
- public static string NumberToString (string format, NumberFormatInfo nfi, ushort value)
- {
- return NumberToString (format, nfi, maxIntLength, 2, (ulong)value);
- }
-
- public static string NumberToString (string format, NumberFormatInfo nfi, uint value)
- {
- return NumberToString (format, nfi, maxIntLength, 4, (ulong)value);
- }
-
- public static string NumberToString (string format, NumberFormatInfo nfi, ulong value)
- {
- return NumberToString (format, nfi, maxLongLength, 8, value);
- }
-
- public static string NumberToString (string format, NumberFormatInfo nfi, int maxLength, int integerSize, ulong value)
- {
- char specifier;
- int precision;
- bool custom;
-
- if (!ParseFormat (format, out specifier, out precision, out custom))
- throw new FormatException (Locale.GetText ("The specified format '" + format + "' is invalid"));
-
- if (custom){
- return FormatCustom (format, value, nfi, maxLength);
- }
-
- switch(specifier) {
- case 'c': return FormatCurrency (value, precision, nfi, maxLength);
- case 'C': return FormatCurrency (value, precision, nfi, maxLength);
- case 'd': return FormatDecimal (value, precision, maxLength);
- case 'D': return FormatDecimal (value, precision, maxLength);
- case 'e': return FormatExponential (value, precision, false, maxLength);
- case 'E': return FormatExponential (value, precision, true, maxLength);
- case 'f': return FormatFixedPoint (value, precision, nfi, maxLength);
- case 'F': return FormatFixedPoint (value, precision, nfi, maxLength);
- case 'g': return FormatGeneral (value, precision, nfi, false, maxLength);
- case 'G': return FormatGeneral (value, precision, nfi, true, maxLength);
- case 'n': return FormatNumber (value, precision, nfi, maxLength);
- case 'N': return FormatNumber (value, precision, nfi, maxLength);
- case 'p': return FormatPercent (value, precision, nfi, maxLength);
- case 'P': return FormatPercent (value, precision, nfi, maxLength);
- case 'r': throw new FormatException (Locale.GetText ("The specified format cannot be used in this instance"));
- case 'R': throw new FormatException (Locale.GetText ("The specified format cannot be used in this instance"));
- case 'x': return FormatHexadecimal (value, precision, false, maxLength);
- case 'X': return FormatHexadecimal (value, precision, true, maxLength);
- default:
- throw new FormatException (Locale.GetText ("The specified format '" + format + "' is invalid"));
- }
- }
-
- // ============ Currency Type Formating ============ //
-
- //
- // Currency Format: Used for strings containing a monetary value. The
- // CurrencySymbol, CurrencyGroupSizes, CurrencyGroupSeparator, and
- // CurrencyDecimalSeparator members of a NumberFormatInfo supply
- // the currency symbol, size and separator for digit groupings, and
- // decimal separator, respectively.
- // CurrencyNegativePattern and CurrencyPositivePattern determine the
- // symbols used to represent negative and positive values. For example,
- // a negative value may be prefixed with a minus sign, or enclosed in
- // parentheses.
- // If the precision specifier is omitted
- // NumberFormatInfo.CurrencyDecimalDigits determines the number of
- // decimal places in the string. Results are rounded to the nearest
- // representable value when necessary.
- //
- // The pattern of the NumberFormatInfo determines how the output looks, where
- // the dollar sign goes, where the negative sign goes, etc.
- // IFormattable documentation lists the patterns and their values,
- // I have them commented out in the large switch statement
- //
-
- static string FormatCurrency (long value, int precision, NumberFormatInfo nfi, int maxLength)
- {
- int i, j, k;
- bool negative = (value < 0);
-
- string groupSeparator = nfi.CurrencyGroupSeparator;
- string decimalSeparator = nfi.CurrencyDecimalSeparator;
- string currencySymbol = nfi.CurrencySymbol;
- int[] groupSizes = nfi.CurrencyGroupSizes;
- int pattern = negative ? nfi.CurrencyNegativePattern : nfi.CurrencyPositivePattern;
- int symbolLength = currencySymbol.Length;
-
- int padding = (precision >= 0) ? precision : nfi.CurrencyDecimalDigits;
- int size = maxLength + (groupSeparator.Length * maxLength) + padding + 2 +
- decimalSeparator.Length + symbolLength;
- char [] buffy = new char [size];
- int position = size;
-
- // set up the pattern from IFormattible
- if (negative) {
- i = symbolLength;
-
- switch (pattern) {
- case 0: // ($nnn)
- buffy[--position] = ')';
- break;
- // case 1: // -$nnn
- // break;
- // case 2: // $-nnn
- // break;
- case 3: // $nnn-
- buffy[--position] = '-';
- break;
- case 4: // (nnn$)
- buffy[--position] = ')';
- do {
- buffy[--position] = currencySymbol[--i];
- } while (i > 0);
- break;
- case 5: // -nnn$
- do {
- buffy[--position] = currencySymbol[--i];
- } while (i > 0);
- break;
- case 6: // nnn-$
- do {
- buffy[--position] = currencySymbol[--i];
- } while (i > 0);
- buffy[--position] = '-';
- break;
- case 7: // nnn$-
- buffy[--position] = '-';
- do {
- buffy[--position] = currencySymbol[--i];
- } while (i > 0);
- break;
- case 8: // -nnn $
- do {
- buffy[--position] = currencySymbol[--i];
- } while (i > 0);
- buffy[--position] = ' ';
- break;
- // case 9: // -$ nnn
- // break;
- case 10: // nnn $-
- buffy[--position] = '-';
- do {
- buffy[--position] = currencySymbol[--i];
- } while (i > 0);
- buffy[--position] = ' ';
- break;
- case 11: // $ nnn-
- buffy[--position] = '-';
- break;
- // case 12: // $ -nnn
- // break;
- case 13: // nnn- $
- do {
- buffy[--position] = currencySymbol[--i];
- } while (i > 0);
- buffy[--position] = ' ';
- buffy[--position] = '-';
- break;
- case 14: // ($ nnn)
- buffy[--position] = ')';
- break;
- case 15: // (nnn $)
- buffy[--position] = ')';
- do {
- buffy[--position] = currencySymbol[--i];
- } while (i > 0);
- break;
- }
- } else {
- i = symbolLength;
- switch (pattern) {
- // case 0: // $nnn
- // break;
- case 1: // nnn$
- do {
- buffy[--position] = currencySymbol[--i];
- } while (i > 0);
- break;
- // case 2: // $ nnn
- // break;
- case 3: // nnn $
- do {
- buffy[--position] = currencySymbol[--i];
- } while (i > 0);
- buffy[--position] = ' ';
- break;
- }
- }
-
- // right pad it w/ precision 0's
- while (padding-- > 0)
- buffy[--position] = '0';
-
- // put on decimal separator if we moved over and put a 0
- if (position < size && buffy[position] == '0') {
- i = decimalSeparator.Length;
- do {
- buffy[--position] = decimalSeparator[--i];
- } while (i > 0);
- }
-
- // loop through, keeping track of where you are in the
- // group sizes array and putting out the group separator
- // when needed
- j = 0;
- k = groupSizes[j++];
-
- if (negative) {
- if (value <= -10) {
- buffy[--position] = digitLowerTable[-(value % 10)];
- value = value / -10;
-
- if (--k == 0) {
- i = groupSeparator.Length;
- do {
- buffy[--position] = groupSeparator[--i];
- } while (i > 0);
-
- k = (j < groupSizes.Length) ?
- groupSizes[j++] : groupSizes[(groupSizes.Length - 1)];
- }
- } else value = -value;
- }
-
- while (value >= 10) {
- buffy[--position] = digitLowerTable[(value % 10)];
- value /= 10;
-
- if (--k == 0) {
- i = groupSeparator.Length;
- do {
- buffy[--position] = groupSeparator[--i];
- } while (i > 0);
-
- k = (j < groupSizes.Length) ?
- groupSizes[j++] : groupSizes[(groupSizes.Length - 1)];
- }
- }
-
- buffy[--position] = digitLowerTable[value];
-
- // end the pattern on the left hand side
- if (negative) {
- i = symbolLength;
-
- switch (pattern) {
- case 0: // ($nnn)
- do {
- buffy[--position] = currencySymbol[--i];
- } while (i > 0);
- buffy[--position] = '(';
- break;
- case 1: // -$nnn
- do {
- buffy[--position] = currencySymbol[--i];
- } while (i > 0);
- buffy[--position] = '-';
- break;
- case 2: // $-nnn
- buffy[--position] = '-';
- do {
- buffy[--position] = currencySymbol[--i];
- } while (i > 0);
- break;
- case 3: // $nnn-
- do {
- buffy[--position] = currencySymbol[--i];
- } while (i > 0);
- break;
- case 4: // (nnn$)
- buffy[--position] = '(';
- break;
- case 5: // -nnn$
- buffy[--position] = '-';
- break;
- // case 6: // nnn-$
- // break;
- // case 7: // nnn$-
- // break;
- case 8: // -nnn $
- buffy[--position] = '-';
- break;
- // case 9: // -$ nnn
- // break;
- // case 10: // nnn $-
- // break;
- case 11: // $ nnn-
- buffy[--position] = ' ';
- do {
- buffy[--position] = currencySymbol[--i];
- } while (i > 0);
- break;
- case 12: // $ -nnn
- buffy[--position] = '-';
- buffy[--position] = ' ';
- do {
- buffy[--position] = currencySymbol[--i];
- } while (i > 0);
- break;
- // case 13: // nnn- $
- // break;
- case 14: // ($ nnn)
- buffy[--position] = ' ';
- do {
- buffy[--position] = currencySymbol[--i];
- } while (i > 0);
- buffy[--position] = '(';
- break;
- case 15: // (nnn $)
- buffy[--position] = '(';
- break;
- }
- } else {
- i = symbolLength;
- switch (pattern) {
- case 0: // $nnn
- do {
- buffy[--position] = currencySymbol[--i];
- } while (i > 0);
- break;
- // case 1: // nnn$
- // break;
- case 2: // $ nnn
- buffy[--position] = ' ';
- do {
- buffy[--position] = currencySymbol[--i];
- } while (i > 0);
- break;
- // case 3: // nnn $
- // break;
- }
- }
-
- return new string (buffy, position, (size - position));
- }
-
- static string FormatCurrency (ulong value, int precision, NumberFormatInfo nfi, int maxLength)
- {
- int i, j, k;
-
- string groupSeparator = nfi.CurrencyGroupSeparator;
- string decimalSeparator = nfi.CurrencyDecimalSeparator;
- string currencySymbol = nfi.CurrencySymbol;
- int[] groupSizes = nfi.CurrencyGroupSizes;
- int pattern = nfi.CurrencyPositivePattern;
- int symbolLength = currencySymbol.Length;
-
- int padding = (precision >= 0) ? precision : nfi.CurrencyDecimalDigits;
- int size = maxLength + (groupSeparator.Length * maxLength) + padding + 2 +
- decimalSeparator.Length + symbolLength;
- char [] buffy = new char [size];
- int position = size;
-
- // set up the pattern from IFormattible, no negative
- i = symbolLength;
- switch (pattern) {
- // case 0: // $nnn
- // break;
- case 1: // nnn$
- do {
- buffy[--position] = currencySymbol[--i];
- } while (i > 0);
- break;
- // case 2: // $ nnn
- // break;
- case 3: // nnn $
- do {
- buffy[--position] = currencySymbol[--i];
- } while (i > 0);
- buffy[--position] = ' ';
- break;
- }
-
- // right pad it w/ precision 0's
- while (padding-- > 0)
- buffy[--position] = '0';
-
- // put on decimal separator if we moved over and put a 0
- if (position < size && buffy[position] == '0') {
- i = decimalSeparator.Length;
- do {
- buffy[--position] = decimalSeparator[--i];
- } while (i > 0);
- }
-
- // loop through, keeping track of where you are in the
- // group sizes array and putting out the group separator
- // when needed
- j = 0;
- k = groupSizes[j++];
-
- while (value >= 10) {
- buffy[--position] = digitLowerTable[(value % 10)];
- value /= 10;
-
- if (--k == 0) {
- i = groupSeparator.Length;
- do {
- buffy[--position] = groupSeparator[--i];
- } while (i > 0);
-
- k = (j < groupSizes.Length) ?
- groupSizes[j++] : groupSizes[(groupSizes.Length - 1)];
- }
- }
-
- buffy[--position] = digitLowerTable[value];
-
- // end the pattern on the left hand side
- i = symbolLength;
- switch (pattern) {
- case 0: // $nnn
- do {
- buffy[--position] = currencySymbol[--i];
- } while (i > 0);
- break;
- // case 1: // nnn$
- // break;
- case 2: // $ nnn
- buffy[--position] = ' ';
- do {
- buffy[--position] = currencySymbol[--i];
- } while (i > 0);
- break;
- // case 3: // nnn $
- // break;
- }
-
- return new string (buffy, position, (size - position));
- }
-
- // ============ Format Decimal Types ============ //
-
- //
- // Used only for integral data types. Negative values are
- // represented by using a '-' sign. The precision specifies
- // how many digits are to appear in the string. If it is >
- // how many digits we need, the left side is padded w/ 0's.
- // If it is smaller than what we need, it is discarded.
- //
- // Fairly simple implementation. Fill the buffer from right
- // to left w/ numbers, then if we still have precision left
- // over, pad w/ zeros.
- //
-
- internal static string FormatDecimal (long value, int precision, int maxLength)
- {
- int size = (precision > 0) ? (maxLength + precision) : maxLength;
- char [] buffy = new char [size];
- int position = size;
- bool negative = (value < 0);
-
- if (negative)
- if (value <= -10) {
- buffy[--position] = digitLowerTable[-(value % 10)];
- value = value / -10;
- } else value = -value;
-
- // get our value into a buffer from right to left
- while (value >= 10) {
- buffy[--position] = digitLowerTable[(value % 10)];
- value /= 10;
- }
-
- buffy[--position] = digitLowerTable[value];
-
- // if we have precision left over, fill with 0's
- precision -= (size - position);
- while (precision-- > 0 && position > 1)
- buffy[--position] = '0';
-
- if (negative)
- buffy[--position] = '-';
-
- return new string (buffy, position, (size - position));
- }
-
- internal static string FormatDecimal (ulong value, int precision, int maxLength)
- {
- int size = (precision > 0) ? (maxLength + precision) : maxLength;
- char [] buffy = new char [size];
- int position = size;
-
- // get our value into a buffer from right to left
- while (value >= 10) {
- buffy[--position] = digitLowerTable[(value % 10)];
- value /= 10;
- }
-
- buffy[--position] = digitLowerTable[value];
-
- // if we have precision left over, fill with 0's
- precision -= (size - position);
- while (precision-- > 0 && position > 1)
- buffy[--position] = '0';
-
- return new string (buffy, position, (size - position));
- }
-
- // ============ Format Exponentials ============ //
-
- //
- // Used for strings in the format [-]M.DDDDDDe+XXX.
- // Exaclty one non-zero digit must appear in M, w/
- // a '-' sign if negative. The precision determines
- // number of decimal places, if not given go 6 places.
- // If precision > the number of places we need, it
- // is right padded w/ 0's. If it is smaller than what
- // we need, we cut off and round. The format specifier
- // decides whether we use an uppercase E or lowercase e.
- //
- // Tried to do this in one pass of one buffer, but it
- // wasn't happening. Get a buffer + 7 extra slots for
- // the -, ., E, +, and XXX. Parse the value into another
- // temp buffer, then build the new string. For the
- // integral data types, there are a couple things that
- // can be hardcoded. Since an int and a long can't be
- // larger than 20 something spaces, the first X w/
- // always be 0, and the the exponential value will only
- // be 2 digits long. Also integer types w/ always
- // have a positive exponential.
- //
-
- static string FormatExponential (long value, int precision, bool upper, int maxLength)
- {
- bool negative = (value < 0);
- int padding = (precision >= 0) ? precision : 6;
- char [] buffy = new char [(padding + 8)];
- char [] tmp = new char [maxLength];
- int exponent = 0, position = maxLength;
- int exp = 0, idx = 0;
- ulong pow = 10;
-
- // ugly, but doing it since abs(Int32.MinValue) > Int.MaxValue
- ulong number = (negative) ? (ulong)((-(value + 1)) + 1) : (ulong)value;
-
- // need to calculate the number of places to know if we need to round later
- if (negative && value <= -10) {
- value /= -10;
- exp++;
- }
-
- while (value >= 10) {
- value /= 10;
- exp++;
- }
-
- if (exp > padding) {
-
- // highest number we should goto before we round
- while (idx++ <= padding)
- pow *= 10;
-
- // get our value into a buffer
- while (number > pow) {
- tmp[--position] = digitLowerTable[(number % 10)];
- number /= 10;
- exponent++;
- }
-
- number += 5;
- }
-
- while (number >= 10) {
- tmp[--position] = digitLowerTable[(number% 10)];
- number /= 10;
- exponent++;
- }
-
- tmp[--position] = digitLowerTable[number];
- idx = 0;
-
- // go left to right in filling up new string
- if (negative)
- buffy[idx++] = '-';
-
- // we know we have at least one in there, followed
- // by a decimal point
- buffy[idx++] = tmp[position++];
- if (precision != 0)
- buffy[idx++] = '.';
-
- // copy over the remaining digits until we run out,
- // or we've passed our specified precision
- while (padding > 0 && position < maxLength) {
- buffy[idx++] = tmp[position++];
- padding--;
- }
-
- // if we still have more precision to go, add some
- // zeros
- while (padding > 0) {
- buffy[idx++] = '0';
- padding--;
- }
-
- // we know these next 3 spots
- buffy[idx++] = upper ? 'E' : 'e';
- buffy[idx++] = '+';
- buffy[idx++] = '0';
-
- // next two digits depend on our length
- if (exponent >= 10) {
- buffy[idx++] = digitLowerTable[(exponent / 10)];
- buffy[idx] = digitLowerTable[(exponent % 10)];
- } else {
- buffy[idx++] = '0';
- buffy[idx] = digitLowerTable[exponent];
- }
-
- return new string(buffy, 0, ++idx);
- }
-
- private static string FormatExponential (ulong value, int precision, bool upper, int maxLength)
- {
- int padding = (precision >= 0) ? precision : 6;
- char [] buffy = new char [(padding + 8)];
- char [] tmp = new char [maxLength];
- int exponent = 0, position = maxLength;
- int exp = 0, idx = 0;
- ulong pow = 10;
- ulong number = value;
-
- // need to calculate the number of places to know if we need to round later
- while (value >= 10) {
- value /= 10;
- exp++;
- }
-
- if (exp > padding) {
-
- // highest number we should goto before we round
- while (idx++ <= padding)
- pow *= 10;
-
- // get our value into a buffer
- while (number > pow) {
- tmp[--position] = digitLowerTable[(number % 10)];
- number /= 10;
- exponent++;
- }
-
- number += 5;
- }
-
- while (number >= 10) {
- tmp[--position] = digitLowerTable[(number% 10)];
- number /= 10;
- exponent++;
- }
-
- tmp[--position] = digitLowerTable[number];
- idx = 0;
-
- // we know we have at least one in there, followed
- // by a decimal point
- buffy[idx++] = tmp[position++];
- if (precision != 0)
- buffy[idx++] = '.';
-
- // copy over the remaining digits until we run out,
- // or we've passed our specified precision
- while (padding > 0 && position < maxLength) {
- buffy[idx++] = tmp[position++];
- padding--;
- }
-
- // if we still have more precision to go, add some
- // zeros
- while (padding > 0) {
- buffy[idx++] = '0';
- padding--;
- }
-
- // we know these next 3 spots
- buffy[idx++] = upper ? 'E' : 'e';
- buffy[idx++] = '+';
- buffy[idx++] = '0';
-
- // next two digits depend on our length
- if (exponent >= 10) {
- buffy[idx++] = digitLowerTable[(exponent / 10)];
- buffy[idx] = digitLowerTable[(exponent % 10)];
- } else {
- buffy[idx++] = '0';
- buffy[idx] = digitLowerTable[exponent];
- }
-
- return new string(buffy, 0, ++idx);
- }
-
- // ============ Format Fixed Points ============ //
-
- //
- // Used for strings in the following form "[-]M.DD...D"
- // At least one non-zero digit precedes the '.', w/ a
- // '-' before that if negative. Precision specifies number
- // of decimal places 'D' to go. If not given, use
- // NumberFormatInfo.NumbeDecimalDigits. Results are rounded
- // if necessary.
- //
- // Fairly simple implementation for integral types. Going
- // from right to left, fill up precision number of 0's,
- // plop a . down, then go for our number.
- //
-
- static string FormatFixedPoint (long value, int precision, NumberFormatInfo nfi, int maxLength)
- {
- int padding = (precision >= 0) ? (precision + maxLength) : (nfi.NumberDecimalDigits + maxLength);
- char [] buffy = new char [padding];
- int position = padding;
- bool negative = (value < 0);
-
- // fill up w/ precision # of 0's
- while (position > (maxLength - 1))
- buffy[--position] = '0';
-
- if (precision != 0)
- buffy[position--] = '.';
-
- if (negative)
- if (value <= -10) {
- buffy[position--] = digitLowerTable[-(value % 10)];
- value = value / -10;
- } else value = -value;
-
- // fill up w/ the value
- while (value >= 10) {
- buffy[position--] = digitLowerTable[(value % 10)];
- value = value / 10;
- }
-
- buffy[position] = digitLowerTable[value];
-
- if (negative)
- buffy[--position] = '-';
-
- return new string (buffy, position, (padding - position));
- }
-
-
- static string FormatFixedPoint (ulong value, int precision, NumberFormatInfo nfi, int maxLength)
- {
- int padding = (precision >= 0) ? (precision + maxLength) : (nfi.NumberDecimalDigits + maxLength);
- char [] buffy = new char [padding];
- int position = padding;
-
- // fill up w/ precision # of 0's
- while (position > (maxLength - 1))
- buffy[--position] = '0';
-
- if (precision != 0)
- buffy[position--] = '.';
-
- // fill up w/ the value
- while (value >= 10) {
- buffy[position--] = digitLowerTable[(value % 10)];
- value = value / 10;
- }
-
- buffy[position] = digitLowerTable[value];
-
- return new string (buffy, position, (padding - position));
- }
-
- // ============ Format General ============ //
-
- //
- // Strings are formatted in either Fixed Point or Exponential
- // format. Results are rounded when needed. If no precision is
- // given, the defaults are:
- //
- // short & ushort: 5
- // int & uint: 10
- // long & ulong: 19
- // float: 7
- // double: 15
- // decimal: 29
- //
- // The value is formatted using fixed-point if exponent >= -4
- // and exponent < precision, where exponent is he exponenent of
- // the value in exponential format. The decimal point and trailing
- // zeros are removed when possible.
- //
- // For all other values, exponential format is used. The case of
- // the format specifier determines whether 'e' or 'E' prefixes
- // the exponent.
- //
- // In either case, the number of digits that appear in the result
- // (not including the exponent) will not exceed the value of the
- // precision. The result is rounded as needed.
- //
- // Integral values are formatted using Fixed Point whenever
- // precision is omitted. (This actually doesn't make sense when
- // coupled w/ the 1st paragraph).
- //
- // Okay, so the decimal point is removed along with any trailing
- // zeros. So, ignoring the last paragraph, we can consider an int
- // ToString() to format it w/ exponential format w/ a default
- // precision of 10, but since it will just be .00000000, it's
- // discarded.
- //
-
- internal static string FormatGeneral (long value, int precision, NumberFormatInfo nfi, bool upper)
- {
- return FormatGeneral (value, precision, nfi, upper, maxIntLength);
- }
-
- internal static string FormatGeneral (long value, int precision, NumberFormatInfo nfi, bool upper, int maxLength)
- {
- bool negative = (value < 0);
- char [] tmp = new char [maxLength];
- int exponent = 0;
- int position = maxLength;
-
- // ugly, but doing it since abs(Int32.MinValue) > Int.MaxValue
- ulong number = (negative) ? (ulong)(-(value + 1) + 1) : (ulong)value;
-
- // get number into a buffer, going to be doing this no matter what
- if (negative)
- if (value <= -10) {
- tmp[--position] = digitLowerTable[-(value % 10)];
- value /= -10;
- } else value = -value;
-
- while (value >= 10) {
- tmp[--position] = digitLowerTable[(value % 10)];
- value /= 10;
- }
-
- tmp[--position] = digitLowerTable[value];
- exponent = (maxLength - position) - 1;
-
- // integral values are formatted using fixed point when precision
- // is not specified. But also trailing decimal point and zeros are
- // discared. So for int's it will always be .00, so just compute
- // here and save the call to FormatFixedPoint & trim.
- if (precision <= 0 || exponent < precision) {
- if (negative)
- tmp[--position] = '-';
-
- return new string (tmp, position, (maxLength - position));
- }
-
- // else our exponent was > precision, use exponential format
- // precision = number of digits to show.
- int idx = 0;
- ulong pow = 1;
-
- exponent = 0;
- position = maxLength;
-
- // Loop through while our number is less than the 10 ^ precision, then
- // add 5 to that to round it out, and keep continuing
- while (idx++ <= precision)
- pow *= 10;
-
- while (number > pow) {
- tmp[--position] = digitLowerTable[(number % 10)];
- number /= 10;
- exponent++;
- }
-
- number += 5;
-
- while (number >= 10) {
- tmp[--position] = digitLowerTable[(number % 10)];
- number /= 10;
- exponent++;
- }
-
- tmp[--position] = digitLowerTable[number];
-
- // finally, make our final buffer, at least precision + 6 for 'E+XX' and '-'
- idx = position;
- position = 0;
- char [] buffy = new char [precision + 6];
-
- if (negative)
- buffy[position++] = '-';
-
- buffy[position++] = tmp[idx++];
- buffy[position] = '.';
-
- // for the remaining precisions copy over rounded tmp
- precision--;
- while (precision-- > 0)
- buffy[++position] = tmp[idx++];
-
- // get rid of ending zeros
- while (buffy[position] == '0')
- position--;
-
- // if we backed up all the way to the ., over write it
- if (buffy[position] != '.')
- position++;
-
- // ints can only be +, e or E depending on format, plus XX
- buffy[position++] = upper ? 'E' : 'e';
- buffy[position++] = '+';
-
- if (exponent >= 10) {
- buffy[position++] = digitLowerTable[(exponent / 10)];
- buffy[position++] = digitLowerTable[(exponent % 10)];
- } else {
- buffy[position++] = '0';
- buffy[position++] = digitLowerTable[exponent];
- }
-
- return new string (buffy, 0, position);
- }
-
- internal static string FormatGeneral (ulong value, int precision, NumberFormatInfo nfi, bool upper, int maxLength)
- {
- char [] tmp = new char [maxLength];
- int exponent = 0;
- int position = maxLength;
- ulong number = value;
-
- // get number into a buffer, going to be doing this no matter what
- while (value >= 10) {
- tmp[--position] = digitLowerTable[(value % 10)];
- value /= 10;
- exponent++;
- }
-
- tmp[--position] = digitLowerTable[value];
-
- // integral values are formatted using fixed point when precision
- // is not specified. But also trailing decimal point and zeros are
- // discared. So for int's it will always be .00, so just compute
- // here and save the call to FormatFixedPoint & trim.
- if (precision <= 0 || exponent < precision)
- return new string (tmp, position, (maxLength - position));
-
- // else our exponent was > precision, use exponential format
- // precision = number of digits to show.
- int idx = 0;
- ulong pow = 1;
-
- exponent = 0;
- position = maxLength;
-
- // Loop through while our number is less than the 10 ^ precision, then
- // add 5 to that to round it out, and keep continuing
- while (idx++ <= precision)
- pow *= 10;
-
- while (number > pow) {
- tmp[--position] = digitLowerTable[(number % 10)];
- number /= 10;
- exponent++;
- }
-
- number += 5;
-
- while (number >= 10) {
- tmp[--position] = digitLowerTable[(number % 10)];
- number /= 10;
- exponent++;
- }
-
- tmp[--position] = digitLowerTable[number];
-
- // finally, make our final buffer, at least precision + 6 for 'E+XX' and '-'
- idx = position;
- position = 0;
- char [] buffy = new char [precision + 6];
-
- buffy[position++] = tmp[idx++];
- buffy[position] = '.';
-
- // for the remaining precisions copy over rounded tmp
- precision--;
- while (precision-- > 0)
- buffy[++position] = tmp[idx++];
-
- // get rid of ending zeros
- while (buffy[position] == '0')
- position--;
-
- // if we backed up all the way to the ., over write it
- if (buffy[position] != '.')
- position++;
-
- // ints can only be +, e or E depending on format, plus XX
- buffy[position++] = upper ? 'E' : 'e';
- buffy[position++] = '+';
-
- if (exponent >= 10) {
- buffy[position++] = digitLowerTable[(exponent / 10)];
- buffy[position++] = digitLowerTable[(exponent % 10)];
- } else {
- buffy[position++] = '0';
- buffy[position++] = digitLowerTable[exponent];
- }
-
- return new string (buffy, 0, position);
- }
-
- // ============ Format Number ============ //
-
- //
- // Used for strings in the following form "[-]d,ddd,ddd.dd...d"
- // The minus sign only appears if it is negative. At least one
- // non-zero digit preceeds the decimal separator. The precision
- // specifier determines the number of decimal places. If it is
- // not given, use NumberFormatInfo.NumberDecimalDigits.
- // The NumberGroupSizes, NumberGroupSeparator, and NumberDecimalSeparator
- // members of NumberFormatInfo supply the size and separator
- // for digit groupings. See IFormattable.
- //
- // The group sizes is an array of ints that determine the grouping
- // of numbers. All digits are in the range 1-9, with the last digit
- // being between 0-9. The number formats the string backwards, with
- // the last digit being the group size for the rest of (leftmost) the
- // the string, 0 being none.
- //
- // For instance:
- // groupSizes = { 3, 2, 1, 0 };
- // int n = 1234567890 => "1234,5,67,890"
- // groupSizes = { 3, 2, 1 };
- // int n = 1234567890 => "1,2,3,4,5,67,890"
- // groupSizes = { 2, 0 };
- // int n = 1234567890 => "1234567,90";
- //
- // Not too difficult, jsut keep track of where you are in the array
- // and when to print the separator
- //
- // The max size of the buffer is assume we have a separator every
- // number, plus the precision on the end, plus a spot for the negative
- // and a spot for decimal separator.
- //
-
- static string FormatNumber (long value, int precision, NumberFormatInfo nfi, int maxLength)
- {
- int i, j, k;
- string groupSeparator = nfi.NumberGroupSeparator;
- string decimalSeparator = nfi.NumberDecimalSeparator;
- int[] groupSizes = nfi.NumberGroupSizes;
-
- int padding = (precision >= 0) ? precision : nfi.NumberDecimalDigits;
- int pattern = nfi.NumberNegativePattern;
- int size = maxLength + (maxLength * groupSeparator.Length) + padding +
- decimalSeparator.Length + 4;
- char [] buffy = new char [size];
- int position = size;
- bool negative = (value < 0);
-
- // pattern for negative values, defined in NumberFormatInfo
- if (negative) {
- switch (pattern) {
- case 0: // (nnn)
- buffy[--position] = ')';
- break;
- // case 1: // -nnn
- // break;
- // case 2: // - nnn
- // break;
- case 3: // nnn-
- buffy[--position] = '-';
- break;
- case 4: // nnn -
- buffy[--position] = '-';
- buffy[--position] = ' ';
- break;
- }
- }
-
- // right pad it w/ precision 0's
- while (padding-- > 0)
- buffy[--position] = '0';
-
- // put on decimal separator
- if (position != size) {
- i = decimalSeparator.Length;
- do {
- buffy[--position] = decimalSeparator[--i];
- } while (i > 0);
- }
-
- // loop through, keeping track of where you are in the
- // group sizes array and putting out the group separator
- // when needed
- j = 0;
- k = groupSizes[j++];
-
- // negative hack for numbers past MinValue
- if (negative)
- if (value <= -10) {
- buffy[--position] = digitLowerTable[-(value % 10)];
- value = value / -10;
-
- if (--k == 0) {
- i = groupSeparator.Length;
- do {
- buffy[--position] = groupSeparator[--i];
- } while (i > 0);
-
- k = (j < groupSizes.Length) ?
- groupSizes[j++] : groupSizes[(groupSizes.Length - 1)];
- }
- } else value = -value;
-
- while (value >= 10) {
- buffy[--position] = digitLowerTable[(value % 10)];
- value /= 10;
-
- if (--k == 0) {
- i = groupSeparator.Length;
- do {
- buffy[--position] = groupSeparator[--i];
- } while (i > 0);
-
- k = (j < groupSizes.Length) ?
- groupSizes[j++] : groupSizes[(groupSizes.Length - 1)];
- }
- }
-
- buffy[--position] = digitLowerTable[value];
-
- // pattern for negative values, defined in NumberFormatInfo
- if (negative) {
- switch (pattern) {
- case 0: // (nnn)
- buffy[--position] = '(';
- break;
- case 1: // -nnn
- buffy[--position] = '-';
- break;
- case 2: // - nnn
- buffy[--position] = ' ';
- buffy[--position] = '-';
- break;
- // case 3: // nnn-
- // break;
- // case 4: // nnn -
- // break;
- }
- }
-
- return new string (buffy, position, (size - position));
- }
-
- static string FormatNumber (ulong value, int precision, NumberFormatInfo nfi, int maxLength)
- {
- int i, j, k;
- string groupSeparator = nfi.NumberGroupSeparator;
- string decimalSeparator = nfi.NumberDecimalSeparator;
- int[] groupSizes = nfi.NumberGroupSizes;
-
- int padding = (precision >= 0) ? precision : nfi.NumberDecimalDigits;
- int size = maxLength + (maxLength * groupSeparator.Length) + padding +
- decimalSeparator.Length + 2;
- char [] buffy = new char [size];
- int position = size;
-
- // right pad it w/ precision 0's
- while (padding-- > 0)
- buffy[--position] = '0';
-
- // put on decimal separator
- if (position != size) {
- i = decimalSeparator.Length;
- do {
- buffy[--position] = decimalSeparator[--i];
- } while (i > 0);
- }
-
- // loop through, keeping track of where you are in the
- // group sizes array and putting out the group separator
- // when needed
- j = 0;
- k = groupSizes[j++];
-
- while (value >= 10) {
- buffy[--position] = digitLowerTable[(value % 10)];
- value /= 10;
-
- if (--k == 0) {
- i = groupSeparator.Length;
- do {
- buffy[--position] = groupSeparator[--i];
- } while (i > 0);
-
- k = (j < groupSizes.Length) ?
- groupSizes[j++] : groupSizes[(groupSizes.Length - 1)];
- }
- }
-
- buffy[--position] = digitLowerTable[value];
-
- return new string (buffy, position, (size - position));
- }
-
- // ============ Percent Formatting ============ //
-
- //
- // Percent Format: Used for strings containing a percentage. The
- // PercentSymbol, PercentGroupSizes, PercentGroupSeparator, and
- // PercentDecimalSeparator members of a NumberFormatInfo supply
- // the Percent symbol, size and separator for digit groupings, and
- // decimal separator, respectively.
- // PercentNegativePattern and PercentPositivePattern determine the
- // symbols used to represent negative and positive values. For example,
- // a negative value may be prefixed with a minus sign, or enclosed in
- // parentheses.
- // If no precision is specified, the number of decimal places in the result
- // is set by NumberFormatInfo.PercentDecimalDigits. Results are
- // rounded to the nearest representable value when necessary.
- // The result is scaled by 100 (.99 becomes 99%).
- //
- // The pattern of the number determines how the output looks, where
- // the percent sign goes, where the negative sign goes, etc.
- // IFormattable documentation lists the patterns and their values,
- // I have them commented out in the switch statement
- //
-
- static string FormatPercent (long value, int precision, NumberFormatInfo nfi, int maxLength)
- {
- int i, j, k;
- bool negative = (value < 0);
-
- string groupSeparator = nfi.PercentGroupSeparator;
- string decimalSeparator = nfi.PercentDecimalSeparator;
- string percentSymbol = nfi.PercentSymbol;
- int[] groupSizes = nfi.PercentGroupSizes;
- int pattern = negative ? nfi.PercentNegativePattern : nfi.PercentPositivePattern;
- int symbolLength = percentSymbol.Length;
-
- int padding = (precision >= 0) ? precision : nfi.PercentDecimalDigits;
- int size = maxLength + (groupSeparator.Length * maxLength) + padding + 2 +
- decimalSeparator.Length + symbolLength;
- char [] buffy = new char [size];
- int position = size;
-
- // set up the pattern from IFormattible
- if (negative) {
- i = symbolLength;
-
- switch (pattern) {
- case 0: // -nnn %
- do {
- buffy[--position] = percentSymbol[--i];
- } while (i > 0);
- buffy[--position] = ' ';
- break;
- case 1: // -nnn%
- do {
- buffy[--position] = percentSymbol[--i];
- } while (i > 0);
- break;
- // case 2: // -%nnn
- // break;
- }
- } else {
- i = symbolLength;
- switch (pattern) {
- case 0: // nnn %
- do {
- buffy[--position] = percentSymbol[--i];
- } while (i > 0);
- buffy[--position] = ' ';
- break;
- case 1: // nnn%
- do {
- buffy[--position] = percentSymbol[--i];
- } while (i > 0);
- break;
- // case 2: // %nnn
- // break;
- }
- }
-
- // right pad it w/ precision 0's
- while (padding-- > 0)
- buffy[--position] = '0';
-
- // put on decimal separator if we moved over and put a 0
- if (position < size && buffy[position] == '0') {
- i = decimalSeparator.Length;
- do {
- buffy[--position] = decimalSeparator[--i];
- } while (i > 0);
- }
-
- // loop through, keeping track of where you are in the
- // group sizes array and putting out the group separator
- // when needed
- j = 0;
- k = groupSizes[j++];
-
- // all values are multiplied by 100, so tack on two 0's
- if (value != 0)
- for (int c = 0; c < 2; c++) {
- buffy[--position] = '0';
-
- if (--k == 0) {
- i = groupSeparator.Length;
- do {
- buffy[--position] = groupSeparator[--i];
- } while (i > 0);
-
- k = (j < groupSizes.Length) ?
- groupSizes[j++] : groupSizes[(groupSizes.Length - 1)];
- }
- }
-
- // negative hack for numbers past MinValue
- if (negative)
- if (value <= -10) {
- buffy[--position] = digitLowerTable[-(value % 10)];
- value = value / -10;
-
- if (--k == 0) {
- i = groupSeparator.Length;
- do {
- buffy[--position] = groupSeparator[--i];
- } while (i > 0);
-
- k = (j < groupSizes.Length) ?
- groupSizes[j++] : groupSizes[(groupSizes.Length - 1)];
- }
- } else value = -value;
-
- while (value >= 10) {
- buffy[--position] = digitLowerTable[(value % 10)];
- value /= 10;
-
- if (--k == 0) {
- i = groupSeparator.Length;
- do {
- buffy[--position] = groupSeparator[--i];
- } while (i > 0);
-
- k = (j < groupSizes.Length) ?
- groupSizes[j++] : groupSizes[(groupSizes.Length - 1)];
- }
- }
-
- buffy[--position] = digitLowerTable[value];
-
- // end the pattern on the left hand side
- if (negative) {
- i = symbolLength;
-
- switch (pattern) {
- case 0: // -nnn %
- buffy[--position] = '-';
- break;
- case 1: // -nnn%
- buffy[--position] = '-';
- break;
- case 2: // -%nnn
- do {
- buffy[--position] = percentSymbol[--i];
- } while (i > 0);
- buffy[--position] = '-';
- break;
- }
- } else {
- i = symbolLength;
- switch (pattern) {
- // case 0: // nnn %
- // break;
- // case 1: // nnn%
- // break;
- case 2: // %nnn
- do {
- buffy[--position] = percentSymbol[--i];
- } while (i > 0);
- break;
- }
- }
-
- return new string (buffy, position, (size - position));
- }
-
- static string FormatPercent (ulong value, int precision, NumberFormatInfo nfi, int maxLength)
- {
- int i, j, k;
-
- string groupSeparator = nfi.PercentGroupSeparator;
- string decimalSeparator = nfi.PercentDecimalSeparator;
- string percentSymbol = nfi.PercentSymbol;
- int[] groupSizes = nfi.PercentGroupSizes;
- int pattern = nfi.PercentPositivePattern;
- int symbolLength = percentSymbol.Length;
-
- int padding = (precision >= 0) ? precision : nfi.PercentDecimalDigits;
- int size = maxLength + (groupSeparator.Length * maxLength) + padding + 2 +
- decimalSeparator.Length + symbolLength;
- char [] buffy = new char [size];
- int position = size;
-
- // set up the pattern from IFormattible
- i = symbolLength;
- switch (pattern) {
- case 0: // -nnn %
- do {
- buffy[--position] = percentSymbol[--i];
- } while (i > 0);
- buffy[--position] = ' ';
- break;
- case 1: // -nnn%
- do {
- buffy[--position] = percentSymbol[--i];
- } while (i > 0);
- break;
- // case 2: // -%nnn
- // break;
- }
-
- // right pad it w/ precision 0's
- while (padding-- > 0)
- buffy[--position] = '0';
-
- // put on decimal separator if we moved over and put a 0
- if (position < size && buffy[position] == '0') {
- i = decimalSeparator.Length;
- do {
- buffy[--position] = decimalSeparator[--i];
- } while (i > 0);
- }
-
- // loop through, keeping track of where you are in the
- // group sizes array and putting out the group separator
- // when needed
- j = 0;
- k = groupSizes[j++];
-
- if (value != 0)
- for (int c = 0; c < 2; c++) {
- buffy[--position] = '0';
-
- if (--k == 0) {
- i = groupSeparator.Length;
- do {
- buffy[--position] = groupSeparator[--i];
- } while (i > 0);
-
- k = (j < groupSizes.Length) ?
- groupSizes[j++] : groupSizes[(groupSizes.Length - 1)];
- }
- }
-
- while (value >= 10) {
- buffy[--position] = digitLowerTable[(value % 10)];
- value /= 10;
-
- if (--k == 0) {
- i = groupSeparator.Length;
- do {
- buffy[--position] = groupSeparator[--i];
- } while (i > 0);
-
- k = (j < groupSizes.Length) ?
- groupSizes[j++] : groupSizes[(groupSizes.Length - 1)];
- }
- }
-
- buffy[--position] = digitLowerTable[value];
-
- i = symbolLength;
- switch (pattern) {
- // case 0: // nnn %
- // break;
- // case 1: // nnn%
- // break;
- case 2: // %nnn
- do {
- buffy[--position] = percentSymbol[--i];
- } while (i > 0);
- break;
- }
-
- return new string (buffy, position, (size - position));
- }
-
- // ============ Format Hexadecimal ============ //
-
- //
- // For strings in base 16. Only valid w/ integers. Precision
- // specifies number of digits in the string, if it specifies
- // more digits than we need, left pad w/ 0's. The case of the
- // the format specifier 'X' or 'x' determines lowercase or
- // capital digits in the output.
- //
- // Whew. Straight forward Hex formatting, however only
- // go 8 places max when dealing with an int (not counting
- // precision padding) and 16 when dealing with a long. This
- // is to cut off the loop when dealing with negative values,
- // which will loop forever when you hit -1;
- //
-
- static string FormatHexadecimal (long value, int precision, bool upper, int maxLength, int integerSize)
- {
- if (precision < 0) precision = 0;
- int size = maxLength + precision;
- char [] buffy = new char [size];
- char[] table = upper ? digitUpperTable : digitLowerTable;
- int position = size;
- const long mask = (1 << 4) - 1;
- integerSize *= 2;
-
- // loop through right to left, shifting and looking up
- // our value. If value is negavite stop after 16 F's
- do {
- buffy[--position] = table[(value & mask)];
- value = value >> 4;
- } while (value != 0 && position > (size - integerSize));
-
- // pad w/ 0's if they want more length, if not, ignore
- precision -= (size - position);
- while (precision > 0 && position > 1) {
- buffy[--position] = '0';
- precision--;
- }
-
- return new string(buffy, position, (size - position));
- }
-
- static string FormatHexadecimal (ulong value, int precision, bool upper, int maxLength)
- {
- if (precision < 0) precision = 0;
- int size = maxLength + precision;
- char [] buffy = new char [size];
- char[] table = upper ? digitUpperTable : digitLowerTable;
- int position = size;
- const ulong mask = (1 << 4) - 1;
-
- // loop through right to left, shifting and looking up
- // our value. Don't worry about negative
- do {
- buffy[--position] = table[value & mask];
- value = value >> 4;
- } while (value != 0);
-
- // pad w/ 0's if they want more length, if not, ignore
- precision -= (size - position);
- while (precision > 0 && position > 1) {
- buffy[--position] = '0';
- precision--;
- }
-
- return new string(buffy, position, (size - position));
- }
-
- // ============ Format Custom ============ //
-
- static string FormatCustom (string format, long number, NumberFormatInfo nfi, int maxLength)
- {
- string strnum = FormatGeneral (number, -1, nfi, true, maxLength);
- FormatParse fp = new FormatParse (format, nfi);
- int sign = (number < 0) ? -1 : (number > 0) ? 1 : 0;
- return fp.FormatNumber (strnum, sign);
- }
-
- static string FormatCustom (string format, ulong number, NumberFormatInfo nfi, int maxLength)
- {
- string strnum = FormatGeneral (number, -1, nfi, true, maxLength);
- FormatParse fp = new FormatParse (format, nfi);
- return fp.FormatNumber (strnum, (number == 0) ? 0 : 1);
- }
- }
-
-class FormatSection {
- public int nph;
- public int nphPreDot;
- public int npercent;
- public int ndividers;
- public int ntokens;
- public string [] tokens;
- public int [] TokenTypes;
- public bool HaveDot;
- public bool HaveSci;
- public bool NegSign;
- public bool sciSignAlways = false;
- public int sciDigits;
- public int numCommas;
-}
-
-class FormatParse {
- const int AS_IS = 0;
- const int PH_0 = 1;
- const int PH_NUMBER = 2;
- const int COMMA = 3;
- const int PERCENT = 4;
- const int DIVIDERS = 5;
- const int DOT = 6;
- const int ESCAPE_SEQ = 7;
- const int SCIENTIFIC = 8;
- const int NEW_SECTION = 9;
- const int NEGSIGN = 10;
-
- private FormatSection [] sections = new FormatSection[3];
- private int nsections = 0;
- private int pos; // Position in the format string
- private int group = 0; // Used in FormatPlain to insert a comma between groups of digits
- private bool isNegative;
- private NumberFormatInfo nfi;
-
- private FormatParse ()
- {
- }
-
- public FormatParse (string format, NumberFormatInfo nfi)
- {
- this.nfi = nfi;
- parseFormat (format);
- }
-
- private void FormatSci (char [] digits, ArrayList outputList, int section)
- {
- FormatSection sec = sections [section];
- int tokidx = sec.ntokens - 1;
-
- // Output everything until we get to the SCIENTIFIC
- while (tokidx >= 0 && sec.TokenTypes [tokidx] != SCIENTIFIC){
- outputList.Add ((string) sec.tokens [tokidx--]);
- }
-
- // Exponent
- int exponent = digits.Length - sec.nph;
- outputList.Add ((string) exponent.ToString ());
- if (sec.sciSignAlways && exponent > 0)
- outputList.Add (nfi.PositiveSign);
- outputList.Add ((string) sec.tokens [tokidx--]);
-
- if (exponent < 0) {
- char [] newDigits;
- exponent = -exponent;
- newDigits = new char [digits.Length + exponent];
- Array.Copy (digits, 0, newDigits, exponent, digits.Length);
- for (int i = 0; i < exponent; i++)
- newDigits[i] = '0';
- digits = newDigits;
- }
-
- // Now format the rest
- int digitIdx = 0;
- if (sec.HaveDot)
- FormatDot (digits, ref digitIdx, outputList, section, tokidx, 0);
- else
- FormatPlain (digits, ref digitIdx, outputList, section, tokidx, 0, sec.numCommas > 0);
- }
-
- private void FormatDot (char [] digits, ref int digitIdx, ArrayList outputList,
- int section, int lastToken, int firstToken)
- {
- int tokidx = lastToken;
- int type;
- FormatSection sec = sections [section];
-
- while (tokidx >= firstToken) {
- type = sec.TokenTypes [tokidx];
- if (type == DOT || type == PH_NUMBER || type == PH_0)
- break;
- tokidx--;
- }
-
- if (tokidx > 0) {
- char [] postDotDigits = new char [sec.nph - sec.nphPreDot];
- int max = (postDotDigits.Length > digits.Length) ? digits.Length : postDotDigits.Length;
- Array.Copy (digits, 0, postDotDigits, 0, max);
- int postDotDigitsIdx = 0;
- FormatPlain (postDotDigits, ref postDotDigitsIdx, outputList, section, lastToken, tokidx, false);
- tokidx--;
- digitIdx += max;
- FormatPlain (digits, ref digitIdx, outputList, section, tokidx, 0, sec.numCommas > 0);
- }
- }
-
- private void FormatPlain (char [] digits, ref int digitIdx, ArrayList outputList,
- int section, int lastToken, int firstToken, bool insertComma)
- {
- int tokidx = lastToken;
- int type;
- int leftMostZeroIdx = -1;
- FormatSection sec = sections [section];
-
- while (tokidx >= firstToken) {
- type = sec.TokenTypes [tokidx];
- if (type == PH_0 || type == PH_NUMBER) {
- if (type == PH_0)
- leftMostZeroIdx = tokidx;
- // Console.WriteLine ("group : {0}", group);
- int i = sec.tokens [tokidx].Length - 1;
- while (i >= 0) {
- if (insertComma && group == 3) {
- outputList.Add (nfi.NumberGroupSeparator);
- group = 0;
- }
-
- if (digitIdx < digits.Length)
- outputList.Add ((string) digits[digitIdx++].ToString ());
- else
- outputList.Add ("0");
-
- i--;
- if (insertComma)
- group++;
- sec.nph--;
- while (sec.nph == 0 && digitIdx < digits.Length) {
- // Flush the numbers left
- if (insertComma && group == 3){
- outputList.Add (nfi.NumberGroupSeparator);
- group = 0;
- }
- outputList.Add ((string) digits [digitIdx++].ToString ());
- if (insertComma)
- group++;
- }
-
- if (sec.nph == 0 && isNegative){
- if ((nsections > 0 && sections [1].NegSign) || nsections == 0)
- outputList.Add (nfi.NegativeSign);
- }
- }
- } else {
- outputList.Add ((string) sec.tokens [tokidx]);
- }
- tokidx--;
- }
-
- // Remove any insignificant zeros we might have at the end
- // if we never saw any zeros in the format string, leftMostZeroIdx will be -1
- if (leftMostZeroIdx < 0)
- while ((string)outputList[outputList.Count-1] == "0")
- outputList.RemoveAt (outputList.Count-1);
- else
- while (leftMostZeroIdx-- > 0)
- if ((string)outputList[outputList.Count-1] == "0")
- outputList.RemoveAt (outputList.Count-1);
- }
-
- private char [] AdjustDigits (string number, FormatSection sec)
- {
- char [] digits = number.ToCharArray ();
- char [] newDigits = digits;
- int decPointIdx = 0;
- int postDot = 0;
-
- decPointIdx -= sec.ndividers * 3;
- decPointIdx += sec.npercent * 2;
- if (sec.HaveDot){
- postDot = sec.nph - sec.nphPreDot;
- decPointIdx += postDot;
- }
-
- if (decPointIdx > 0) {
- newDigits = new char [digits.Length + decPointIdx];
- Array.Copy (digits, 0, newDigits, 0, digits.Length);
- for (int i = 0; i < decPointIdx; i++)
- newDigits[digits.Length + i] = '0';
- } else if (decPointIdx < 0) {
- decPointIdx = -decPointIdx;
- if (decPointIdx >= digits.Length) {
- if (sec.HaveSci){
- } else {
- // The numbers turns into 0 when formatting applied
- digits = new char [1] {'0'};
- }
- } else {
- int newLength = digits.Length - decPointIdx + postDot - 1;
- newDigits = new char [newLength];
- int max = digits.Length >= newLength ? newLength : digits.Length;
- Array.Copy (digits, 0, newDigits, 0, max);
- if (newLength > digits.Length)
- for (int i = 0; i < decPointIdx; i++)
- newDigits[digits.Length + i] = '0';
- }
- }
-
- return newDigits;
- }
-
- public string FormatNumber (string number, int signValue)
- {
- char [] digits;
-
- isNegative = signValue < 0;
- int section = 0;
- if (signValue < 0 && nsections > 0)
- section = 1;
- if (signValue == 0 && nsections > 1)
- section = 2;
-
- if (number [0] == '-')
- number = number.Substring (1);
-
- FormatSection sec = sections [section];
- digits = AdjustDigits (number.ToString (), sec);
-
- if (digits.Length == 1 && digits [0] == '0')
- if (nsections > 2)
- sec = sections [2]; // Format as a 0
- else
- sec = sections [0]; // Format as positive
-
- ArrayList outputList = new ArrayList ();
-
- int digitIdx = 0;
- Array.Reverse (digits);
-
- if (sec.HaveSci)
- FormatSci (digits, outputList, section);
- else if (sec.HaveDot)
- FormatDot (digits, ref digitIdx, outputList, section, sec.ntokens - 1, 0);
- else
- FormatPlain (digits, ref digitIdx, outputList, section, sec.ntokens - 1, 0, sec.numCommas > 0);
-
- string result = "";
- for (int i = outputList.Count - 1; i >= 0; i--) {
- result += (string) outputList[i];
- }
-
- return result;
- }
-
- private void parseFormat (string format)
- {
- char [] fmt_chars = format.ToCharArray ();
- int fmtlen = fmt_chars.Length;
- int type = AS_IS;
- int prevType = AS_IS;
- string token;
- bool tokens_seen = false;
-
- sections[0] = new FormatSection();
- while (pos < fmtlen) {
- token = getNextToken (fmt_chars, fmtlen, out type, nsections == 1 && !tokens_seen);
- if (type == NEW_SECTION) {
- nsections++;
- if (nsections > 3)
- break;
- sections[nsections] = new FormatSection();
- tokens_seen = false;
- } else {
- if (type != AS_IS)
- tokens_seen = true;
- prevType = AddToken (token, type, prevType);
- }
- }
- }
-
- private int AddToken (string token, int type, int prevType)
- {
- FormatSection sec = sections[nsections];
- string [] newTokens = new string [sec.ntokens + 1];
- int [] newTokenTypes = new int [sec.ntokens + 1];
- for (int i = 0; i < sec.ntokens; i++) {
- newTokens[i] = sec.tokens[i];
- newTokenTypes[i] = sec.TokenTypes[i];
- }
-
- switch (type) {
- case ESCAPE_SEQ :
- type = AS_IS;
- break;
- case COMMA :
- if (!sec.HaveDot && (prevType == PH_0 || prevType == PH_NUMBER)) {
- sec.numCommas++;
- } else
- type = AS_IS;
-
- token = "";
- break;
- case DOT :
- if (!sec.HaveDot && (prevType == PH_0 || prevType == PH_NUMBER ||
- prevType == DIVIDERS || prevType == COMMA)) {
- sec.HaveDot = true;
- sec.nphPreDot = sec.nph;
- } else
- type = AS_IS;
-
- break;
- case PERCENT :
- sec.npercent++;
- break;
- case DIVIDERS :
- token = "";
- if (!sec.HaveDot)
- sec.ndividers = token.Length;
- else
- type = AS_IS;
- break;
- case PH_0 :
- if (!sec.HaveSci)
- sec.nph += token.Length;
- else
- type = AS_IS;
- break;
- case PH_NUMBER :
- if (!sec.HaveSci)
- sec.nph += token.Length;
- else
- type = AS_IS;
- break;
- case SCIENTIFIC :
- if (!sec.HaveSci && sec.nph > 0) {
- sec.HaveSci = true;
- char [] sci = token.ToCharArray ();
- sec.sciSignAlways = sci[1] == '+' ? true : false;
- int expLen = sci[1] == '0' ? token.Length - 1 : token.Length - 2;
- sec.sciDigits = expLen;
- token = sci[0].ToString ();
- } else {
- type = AS_IS;
- }
- break;
-
- case NEGSIGN:
- sec.NegSign = true;
- break;
- }
-
- newTokens[sec.ntokens] = token;
- newTokenTypes[sec.ntokens] = type;
- sec.tokens = newTokens;
- sec.TokenTypes = newTokenTypes;
- sec.ntokens++;
- return type;
- }
-
- private string getNextToken (char [] fmt_chars, int fmtlen, out int type, bool negsection)
- {
- int curpos = pos;
- string result = null;
- char current;
-
- type = AS_IS; // Default
- current = fmt_chars[curpos];
- if (current == ';'){
- type = NEW_SECTION;
- result = "NEW_SECTION";
- pos++;
- }
- else if (current == '\'' || current == '"') {
- curpos++;
- int endpos = Array.IndexOf (fmt_chars, current, curpos);
- if (endpos == -1)
- endpos = fmtlen;
- result = new string (fmt_chars, curpos, endpos - curpos);
- pos = endpos + 1;
- }
- else if (current == '\\') { //MS seems not to translate escape seqs!
- type = ESCAPE_SEQ;
- current = fmt_chars[++pos];
- result = current.ToString ();
- pos++;
- }
- else if (current == '%') {
- type = PERCENT;
- result = nfi.PercentSymbol;
- pos++;
- }
- else if (current == '.') {
- type = DOT;
- result = nfi.NumberDecimalSeparator;
- pos++;
- }
- else if (current == ',') {
- int begpos = curpos;
-
- while (++curpos < fmtlen && fmt_chars[curpos] == ',');
- if (curpos == fmtlen || fmt_chars[curpos] == '.') {
- // ,,,,
- result = new string (fmt_chars, begpos, curpos - begpos);
- type = DIVIDERS;
- pos = curpos;
- } else {
- result = ",";
- type = COMMA;
- pos++;
- }
- }
- else if (current == '0' || current == '#') {
- char placeHolder = current;
- int begpos = curpos;
- type = placeHolder == '0' ? PH_0 : PH_NUMBER;
- curpos++;
- while (curpos < fmtlen && fmt_chars [curpos] == placeHolder)
- curpos++;
- result = new string (fmt_chars, begpos, curpos - begpos);
- pos = curpos;
- }
- else if (current == 'e' || current == 'E') {
- if (fmtlen <= curpos + 1){
- result = current.ToString ();
- pos++;
- }
- else {
- char next1 = fmt_chars [curpos + 1];
-
- if (next1 != '-' && next1 != '+' && next1 != '0') {
- result = new string (fmt_chars, curpos, 2);
- pos += 2;
- }
- else {
- int begpos = curpos;
-
- if (next1 == '-' || next1 == '+')
- curpos++;
-
- curpos++;
-
- if (curpos < fmtlen && fmt_chars [curpos] == '0'){
- type = SCIENTIFIC;
- while (curpos < fmtlen && fmt_chars [curpos] == '0')
- curpos++;
- }
-
- result = new string (fmt_chars, begpos, curpos - begpos);
- pos = curpos;
- }
- }
- }
- else if (negsection && current == '-'){
- type = NEGSIGN;
- pos++;
- }
- else {
- char [] format_spec = { '0', '#', ',', '.', '%', 'E', 'e', '"', '\'', '\\' };
- int nextFE;
-
- while (curpos < fmtlen) {
- current = fmt_chars[curpos];
- nextFE = Array.IndexOf (format_spec, current);
- if (nextFE != -1)
- break;
- curpos++;
- }
-
- result = new string (fmt_chars, pos, curpos - pos);
- pos = curpos;
- }
-
- return result;
- }
-}
-
-}
--- /dev/null
+//
+// System.NumberFormatter.cs
+//
+// Author:
+// Kazuki Oikawa (kazuki@panicode.com)
+//
+
+using System.Collections;
+using System.Globalization;
+using System.Text;
+
+namespace System
+{
+ class NumberFormatter
+ {
+ static char[] digitLowerTable = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
+ static char[] digitUpperTable = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
+
+ #region NumberToString
+ public static string NumberToString (string format, sbyte value, NumberFormatInfo nfi)
+ {
+ return NumberToString (format, NumberStore.CreateInstance (value), nfi);
+ }
+ public static string NumberToString (string format, byte value, NumberFormatInfo nfi)
+ {
+ return NumberToString (format, NumberStore.CreateInstance (value), nfi);
+ }
+ public static string NumberToString (string format, ushort value, NumberFormatInfo nfi)
+ {
+ return NumberToString (format, NumberStore.CreateInstance (value), nfi);
+ }
+ public static string NumberToString (string format, short value, NumberFormatInfo nfi)
+ {
+ return NumberToString (format, NumberStore.CreateInstance (value), nfi);
+ }
+ public static string NumberToString (string format, uint value, NumberFormatInfo nfi)
+ {
+ return NumberToString (format, NumberStore.CreateInstance (value), nfi);
+ }
+ public static string NumberToString (string format, int value, NumberFormatInfo nfi)
+ {
+ return NumberToString (format, NumberStore.CreateInstance (value), nfi);
+ }
+ public static string NumberToString (string format, ulong value, NumberFormatInfo nfi)
+ {
+ return NumberToString (format, NumberStore.CreateInstance (value), nfi);
+ }
+ public static string NumberToString (string format, long value, NumberFormatInfo nfi)
+ {
+ return NumberToString (format, NumberStore.CreateInstance (value), nfi);
+ }
+ public static string NumberToString (string format, float value, NumberFormatInfo nfi)
+ {
+ return NumberToString (format, NumberStore.CreateInstance (value), nfi);
+ }
+ public static string NumberToString (string format, double value, NumberFormatInfo nfi)
+ {
+ return NumberToString (format, NumberStore.CreateInstance (value), nfi);
+ }
+ public static string NumberToString (string format, NumberStore ns, NumberFormatInfo nfi)
+ {
+ if (ns.IsNaN) {
+ return nfi.NaNSymbol;
+ }
+ if (ns.IsInfinity) {
+ if (ns.Positive)
+ return nfi.PositiveInfinitySymbol;
+ else
+ return nfi.NegativeInfinitySymbol;
+ }
+
+ char specifier;
+ int precision;
+ bool custom;
+
+ if (format == null || format == "")
+ format = "G";
+
+ if (nfi == null)
+ nfi = NumberFormatInfo.GetInstance (null);
+
+ if (!ParseBasicFormat (format, out specifier, out precision, out custom))
+ throw new FormatException (Locale.GetText ("The specified format '" + format + "' is invalid"));
+
+ if (custom){
+ if (ns.IsFloatingSource)
+ ns.RoundEffectiveDigits (ns.DefaultPrecision);
+ return FormatCustom (format, ns, nfi);
+ }
+
+ if (ns.IsFloatingSource) {
+ switch(specifier) {
+ case 'p':
+ case 'P':
+ case 'c':
+ case 'C':
+ case 'f':
+ case 'F':
+ case 'N':
+ case 'n':
+ ns.RoundEffectiveDigits (ns.DefaultPrecision);
+ break;
+ case 'g':
+ case 'G':
+ if (precision <= 0)
+ ns.RoundEffectiveDigits (ns.DefaultPrecision);
+ else
+ ns.RoundEffectiveDigits (precision);
+ break;
+ case 'r':
+ case 'R':
+ ns.RoundEffectiveDigits (ns.DefaultMaxPrecision);
+ break;
+ default:
+ if (precision > ns.DefaultPrecision)
+ ns.RoundEffectiveDigits (precision + 1);
+ else
+ ns.RoundEffectiveDigits (ns.DefaultPrecision + 1);
+ break;
+ }
+ }
+
+ switch(specifier) {
+ case 'c':
+ case 'C':
+ return FormatCurrency (ns, precision, nfi);
+ case 'd':
+ case 'D':
+ return FormatDecimal (ns, precision, nfi);
+ case 'e':
+ case 'E':
+ return FormatExponential (ns, precision, nfi, specifier == 'E');
+ case 'f':
+ case 'F':
+ return FormatFixedPoint (ns, precision, nfi);
+ case 'g':
+ case 'G':
+ return FormatGeneral (ns, precision, nfi, specifier == 'G');
+ case 'n':
+ case 'N':
+ return FormatNumber (ns, precision, nfi);
+ case 'p':
+ case 'P':
+ return FormatPercent (ns, precision, nfi);
+ case 'r':
+ case 'R':
+ if (ns.IsFloatingSource) {
+ return FormatGeneral (ns, ns.DefaultMaxPrecision, nfi, true);
+ } else {
+ throw new FormatException (Locale.GetText ("The specified format cannot be used in this instance"));
+ }
+ case 'x':
+ case 'X': return FormatHexadecimal (ns, precision, nfi, specifier == 'X');
+ default:
+ throw new FormatException (Locale.GetText ("The specified format '" + format + "' is invalid"));
+ }
+ }
+ #endregion
+
+ #region BasicParser
+ private static bool ParseBasicFormat (string format, out char specifier, out int precision, out bool custom)
+ {
+ precision = -1;
+ specifier = '\0';
+ custom = false;
+
+ int length = format.Length;
+ if (length < 1)
+ return false;
+
+ specifier = format [0];
+
+ if (Char.IsLetter (specifier)) {
+ if (format.Length == 1)
+ return true;
+
+ bool flag = true;
+ precision = 0;
+ for (int i = 1; i < format.Length; i++) {
+ char c = format [i];
+ if (char.IsDigit (c)) {
+ precision = precision * 10 + (c - '0');
+ if (precision > 99) {
+ flag = false;
+ break;
+ }
+ }
+ else {
+ flag = false;
+ break;
+ }
+ }
+ if (flag)
+ return true;
+ }
+
+ custom = true;
+ return true;
+ }
+
+ #endregion
+
+ #region Helpers
+ private static void ZeroTrimEnd (StringBuilder sb)
+ {
+ ZeroTrimEnd (sb, false);
+ }
+ private static void ZeroTrimEnd (StringBuilder sb, bool canEmpty)
+ {
+ int len = 0;
+ for (int i = sb.Length - 1; (canEmpty ? i >= 0 : i > 0); i --) {
+ if (sb [i] != '0')
+ break;
+ len ++;
+ }
+
+ if (len > 0)
+ sb.Remove (sb.Length - len, len);
+ }
+ #endregion
+
+ #region Basic
+ internal static string FormatCurrency (NumberStore ns, int precision, NumberFormatInfo nfi)
+ {
+ int[] groups = nfi.CurrencyGroupSizes;
+ string groupSeparator = nfi.CurrencyGroupSeparator;
+ precision = (precision >= 0 ? precision : nfi.CurrencyDecimalDigits);
+ StringBuilder sb = new StringBuilder();
+
+ ns.RoundDecimal (precision);
+ if (precision > 0) {
+ sb.Append (nfi.CurrencyDecimalSeparator);
+ sb.Append (ns.GetDecimalString (precision));
+ }
+
+ string intPart = ns.GetIntegerString (ns.IntegerDigits > 0 ? ns.IntegerDigits : 1);
+ int index = intPart.Length - 1;
+ int counter = 0;
+ int groupIndex = 0;
+ int groupSize = (groups.Length > 0 ? groups [groupIndex++] : 0);
+ while (index >= 0) {
+ sb.Insert (0, intPart [index --]);
+ counter ++;
+
+ if (index >= 0 && groupSize > 0 && counter % groupSize == 0) {
+ sb.Insert (0, groupSeparator);
+ groupSize = (groupIndex < groups.Length ? groups [groupIndex++] : groupSize);
+ counter = 0;
+ }
+ }
+
+ if (ns.Positive || NumberStore.IsZeroOnly (sb)) {
+ switch (nfi.CurrencyPositivePattern) {
+ case 0:
+ sb.Insert (0, nfi.CurrencySymbol);
+ break;
+ case 1:
+ sb.Append (nfi.CurrencySymbol);
+ break;
+ case 2:
+ sb.Insert (0, " ");
+ sb.Insert (0, nfi.CurrencySymbol);
+ break;
+ case 3:
+ sb.Append (" ");
+ sb.Append (nfi.CurrencySymbol);
+ break;
+ }
+ } else {
+ switch (nfi.CurrencyNegativePattern) {
+ case 0:
+ sb.Insert (0, nfi.CurrencySymbol);
+ sb.Insert (0, '(');
+ sb.Append (')');
+ break;
+ case 1:
+ sb.Insert (0, nfi.CurrencySymbol);
+ sb.Insert (0, nfi.NegativeSign);
+ break;
+ case 2:
+ sb.Insert (0, nfi.NegativeSign);
+ sb.Insert (0, nfi.CurrencySymbol);
+ break;
+ case 3:
+ sb.Insert (0, nfi.CurrencySymbol);
+ sb.Append (nfi.NegativeSign);
+ break;
+ case 4:
+ sb.Insert (0, '(');
+ sb.Append (nfi.CurrencySymbol);
+ sb.Append (')');
+ break;
+ case 5:
+ sb.Insert (0, nfi.NegativeSign);
+ sb.Append (nfi.CurrencySymbol);
+ break;
+ case 6:
+ sb.Append (nfi.NegativeSign);
+ sb.Append (nfi.CurrencySymbol);
+ break;
+ case 7:
+ sb.Append (nfi.CurrencySymbol);
+ sb.Append (nfi.NegativeSign);
+ break;
+ case 8:
+ sb.Insert (0, nfi.NegativeSign);
+ sb.Append (' ');
+ sb.Append (nfi.CurrencySymbol);
+ break;
+ case 9:
+ sb.Insert (0, ' ');
+ sb.Insert (0, nfi.CurrencySymbol);
+ sb.Insert (0, nfi.NegativeSign);
+ break;
+ case 10:
+ sb.Append (' ');
+ sb.Append (nfi.CurrencySymbol);
+ sb.Append (nfi.NegativeSign);
+ break;
+ case 11:
+ sb.Insert (0, ' ');
+ sb.Insert (0, nfi.CurrencySymbol);
+ sb.Append (nfi.NegativeSign);
+ break;
+ case 12:
+ sb.Insert (0, nfi.NegativeSign);
+ sb.Insert (0, ' ');
+ sb.Insert (0, nfi.CurrencySymbol);
+ break;
+ case 13:
+ sb.Append (nfi.NegativeSign);
+ sb.Append (' ');
+ sb.Append (nfi.CurrencySymbol);
+ break;
+ case 14:
+ sb.Insert (0, ' ');
+ sb.Insert (0, nfi.CurrencySymbol);
+ sb.Insert (0, '(');
+ sb.Append (')');
+ break;
+ case 15:
+ sb.Insert (0, '(');
+ sb.Append (' ');
+ sb.Append (nfi.CurrencySymbol);
+ sb.Append (')');
+ break;
+ }
+ }
+
+ return sb.ToString ();
+ }
+ internal static string FormatDecimal (NumberStore ns, int precision, NumberFormatInfo nfi)
+ {
+ if (ns.IsFloatingSource)
+ throw new FormatException ();
+
+ precision = precision > 0 ? precision : 1;
+ StringBuilder sb = new StringBuilder();
+ sb.Append (ns.GetIntegerString (ns.IntegerDigits > precision ? ns.IntegerDigits : precision));
+
+ if (!ns.Positive && !NumberStore.IsZeroOnly (sb))
+ sb.Insert (0, nfi.NegativeSign);
+
+ return sb.ToString ();
+ }
+ internal static string FormatFixedPoint (NumberStore ns, int precision, NumberFormatInfo nfi)
+ {
+ precision = precision >= 0 ? precision : nfi.NumberDecimalDigits;
+ ns.RoundDecimal (precision);
+
+ StringBuilder sb = new StringBuilder();
+ sb.Append (ns.GetIntegerString (ns.IntegerDigits > 0 ? ns.IntegerDigits : 1));
+
+ if (precision > 0) {
+ sb.Append (nfi.NumberDecimalSeparator);
+ sb.Append (ns.GetDecimalString (precision));
+ }
+
+ if (!ns.Positive && !NumberStore.IsZeroOnly (sb))
+ sb.Insert (0, nfi.NegativeSign);
+
+ return sb.ToString ();
+ }
+
+ internal static string FormatGeneral (NumberStore ns, int precision, NumberFormatInfo nfi, bool upper)
+ {
+ if (ns.ZeroOnly)
+ return "0";
+
+ precision = precision > 0 ? precision : ns.DefaultPrecision;
+ StringBuilder sb = new StringBuilder();
+
+ int preExponent = 0;
+ NumberStore prens = ns.GetClone ();
+ while (!(prens.DecimalPointPosition == 1 && prens.GetChar (0) != '0')) {
+ if (prens.DecimalPointPosition > 1) {
+ prens.Divide10 (1);
+ preExponent ++;
+ } else {
+ prens.Multiply10 (1);
+ preExponent --;
+ }
+ }
+
+ bool fixedPointMode = preExponent > -5 && preExponent < precision;
+
+ precision = precision < ns.DefaultPrecision + 2 ? precision : ns.DefaultPrecision + 2;
+ precision = precision < 17 ? precision : 17;
+ if (fixedPointMode) {
+ ns.RoundDecimal (precision);
+ } else {
+ ns = prens;
+ if (ns.RoundDecimal (precision - 1)) {
+ ns.Divide10 (1);
+ preExponent ++;
+ }
+ }
+
+ if (!ns.Positive) {
+ sb.Append (nfi.NegativeSign);
+ }
+ sb.Append (ns.GetIntegerString (ns.IntegerDigits > 0 ? ns.IntegerDigits : 1));
+ if (ns.DecimalDigits > 0) {
+ sb.Append (nfi.NumberDecimalSeparator);
+ sb.Append (ns.GetDecimalString (ns.DecimalDigits));
+ }
+
+ if (!fixedPointMode) {
+ if (upper)
+ sb.Append ('E');
+ else
+ sb.Append ('e');
+
+ if (preExponent >= 0)
+ sb.Append (nfi.PositiveSign);
+ else {
+ sb.Append (nfi.NegativeSign);
+ preExponent = -preExponent;
+ }
+
+ if (preExponent < 10)
+ sb.Append ('0');
+
+ int pos = sb.Length;
+ while (preExponent > 0) {
+ sb.Insert (pos, digitLowerTable [preExponent % 10]);
+ preExponent /= 10;
+ }
+ }
+
+ return sb.ToString ();
+ }
+ internal static string FormatNumber (NumberStore ns, int precision, NumberFormatInfo nfi)
+ {
+ int[] groups = nfi.NumberGroupSizes;
+ string groupSeparator = nfi.NumberGroupSeparator;
+ precision = (precision >= 0 ? precision : nfi.NumberDecimalDigits);
+ StringBuilder sb = new StringBuilder();
+
+ ns.RoundDecimal (precision);
+ if (precision > 0) {
+ sb.Append (nfi.NumberDecimalSeparator);
+ sb.Append (ns.GetDecimalString (precision));
+ }
+
+ string intPart = ns.GetIntegerString (ns.IntegerDigits > 0 ? ns.IntegerDigits : 1);
+ int index = intPart.Length - 1;
+ int counter = 0;
+ int groupIndex = 0;
+ int groupSize = (groups.Length > 0 ? groups [groupIndex++] : 0);
+ while (index >= 0) {
+ sb.Insert (0, intPart [index --]);
+ counter ++;
+
+ if (index >= 0 && groupSize > 0 && counter % groupSize == 0) {
+ sb.Insert (0, groupSeparator);
+ groupSize = (groupIndex < groups.Length ? groups [groupIndex++] : groupSize);
+ counter = 0;
+ }
+ }
+
+ if (!ns.Positive && !NumberStore.IsZeroOnly (sb)) {
+ switch (nfi.NumberNegativePattern) {
+ case 0:
+ sb.Insert (0, '(');
+ sb.Append (')');
+ break;
+ case 1:
+ sb.Insert (0, nfi.NegativeSign);
+ break;
+ case 2:
+ sb.Insert (0, ' ');
+ sb.Insert (0, nfi.NegativeSign);
+ break;
+ case 3:
+ sb.Append (nfi.NegativeSign);
+ break;
+ case 4:
+ sb.Append (' ');
+ sb.Append (nfi.NegativeSign);
+ break;
+ }
+ }
+
+ return sb.ToString ();
+ }
+ internal static string FormatPercent (NumberStore ns, int precision, NumberFormatInfo nfi)
+ {
+ int[] groups = nfi.PercentGroupSizes;
+ string groupSeparator = nfi.PercentGroupSeparator;
+ precision = (precision >= 0 ? precision : nfi.PercentDecimalDigits);
+ StringBuilder sb = new StringBuilder();
+
+ ns.Multiply10 (2);
+
+ ns.RoundDecimal (precision);
+ if (precision > 0) {
+ sb.Append (nfi.PercentDecimalSeparator);
+ sb.Append (ns.GetDecimalString (precision));
+ }
+
+ string intPart = ns.GetIntegerString (ns.IntegerDigits > 0 ? ns.IntegerDigits : 1);
+ int index = intPart.Length - 1;
+ int counter = 0;
+ int groupIndex = 0;
+ int groupSize = (groups.Length > 0 ? groups [groupIndex++] : 0);
+ while (index >= 0) {
+ sb.Insert (0, intPart [index --]);
+ counter ++;
+
+ if (index >= 0 && groupSize > 0 && counter % groupSize == 0) {
+ sb.Insert (0, groupSeparator);
+ groupSize = (groupIndex < groups.Length ? groups [groupIndex++] : groupSize);
+ counter = 0;
+ }
+ }
+
+ if (ns.Positive || NumberStore.IsZeroOnly (sb)) {
+ switch (nfi.PercentPositivePattern) {
+ case 0:
+ sb.Append (' ');
+ sb.Append (nfi.PercentSymbol);
+ break;
+ case 1:
+ sb.Append (nfi.PercentSymbol);
+ break;
+ case 2:
+ sb.Insert (0, nfi.PercentSymbol);
+ break;
+ }
+ } else {
+ switch (nfi.PercentNegativePattern) {
+ case 0:
+ sb.Append (' ');
+ sb.Append (nfi.PercentSymbol);
+ sb.Insert (0, nfi.NegativeSign);
+ break;
+ case 1:
+ sb.Append (nfi.PercentSymbol);
+ sb.Insert (0, nfi.NegativeSign);
+ break;
+ case 2:
+ sb.Insert (0, nfi.PercentSymbol);
+ sb.Insert (0, nfi.NegativeSign);
+ break;
+ }
+ }
+
+ return sb.ToString ();
+ }
+ internal static string FormatHexadecimal (NumberStore ns, int precision, NumberFormatInfo nfi, bool upper)
+ {
+ if (ns.IsFloatingSource)
+ throw new FormatException ();
+
+ StringBuilder sb = new StringBuilder();
+
+ int intSize = ns.DefaultByteSize;
+ ulong value = ulong.Parse (ns.GetIntegerString (ns.IntegerDigits > 0 ? ns.IntegerDigits : 1));
+
+ if (!ns.Positive) {
+ value = (ulong)(Math.Pow (2, intSize * 8)) - value;
+ }
+
+ char[] digits = (upper ? digitUpperTable : digitLowerTable);
+
+ while (value > 0) {
+ sb.Insert (0, digits [value % 16]);
+ value >>= 4;
+ }
+
+ if (sb.Length == 0)
+ sb.Append ('0');
+
+ if (sb.Length < precision)
+ sb.Insert (0, "0", precision - sb.Length);
+
+ return sb.ToString ();
+ }
+ internal static string FormatExponential (NumberStore ns, int precision, NumberFormatInfo nfi, bool upper)
+ {
+ if (precision < 0)
+ precision = 6;
+ string decimalPart = (precision > 0 ? string.Concat(".", new string ('0', precision)) : "");
+ return FormatCustom (string.Concat ("0", decimalPart , (upper ? "E": "e"), "+000"), ns, nfi);
+ }
+ #endregion
+
+ #region Custom
+ internal static string FormatCustom (string format, NumberStore ns, NumberFormatInfo nfi)
+ {
+ bool p = ns.Positive;
+ format = CustomInfo.GetActiveSection (format,ref p, ns.ZeroOnly);
+ if (format == "") {
+ return ns.Positive ? "" : nfi.NegativeSign;
+ }
+ ns.Positive = p;
+
+ CustomInfo info = CustomInfo.Parse (format, nfi);
+#if false
+ Console.WriteLine("Format : {0}",format);
+ Console.WriteLine("DecimalDigits : {0}",info.DecimalDigits);
+ Console.WriteLine("DecimalPointPos : {0}",info.DecimalPointPos);
+ Console.WriteLine("DecimalTailSharpDigits : {0}",info.DecimalTailSharpDigits);
+ Console.WriteLine("IntegerDigits : {0}",info.IntegerDigits);
+ Console.WriteLine("IntegerHeadSharpDigits : {0}",info.IntegerHeadSharpDigits);
+ Console.WriteLine("IntegerHeadPos : {0}",info.IntegerHeadPos);
+ Console.WriteLine("UseExponent : {0}",info.UseExponent);
+ Console.WriteLine("ExponentDigits : {0}",info.ExponentDigits);
+ Console.WriteLine("ExponentTailSharpDigits : {0}",info.ExponentTailSharpDigits);
+ Console.WriteLine("ExponentNegativeSignOnly : {0}",info.ExponentNegativeSignOnly);
+ Console.WriteLine("DividePlaces : {0}",info.DividePlaces);
+ Console.WriteLine("Percents : {0}",info.Percents);
+ Console.WriteLine("Permilles : {0}",info.Permilles);
+#endif
+ StringBuilder sb_int = new StringBuilder();
+ StringBuilder sb_dec = new StringBuilder();
+ StringBuilder sb_exp = new StringBuilder();
+
+ int diff = 0;
+ if (info.Percents > 0) {
+ ns.Multiply10 (2 * info.Percents);
+ }
+ if (info.Permilles > 0) {
+ ns.Multiply10 (3 * info.Permilles);
+ }
+ if (info.DividePlaces > 0) {
+ ns.Divide10 (info.DividePlaces);
+ }
+
+ bool expPositive = true;
+ if (info.UseExponent && (info.DecimalDigits > 0 || info.IntegerDigits > 0)) {
+ if (!ns.ZeroOnly) {
+ while (true) {
+ while (ns.IntegerDigits > info.IntegerDigits) {
+ ns.Divide10 (1);
+ diff--;
+ if (ns.IntegerDigits == 1 && ns.GetChar (0) == '0')
+ break;
+ }
+ while (ns.IntegerDigits < info.IntegerDigits || (ns.IntegerDigits == info.IntegerDigits && ns.GetChar (0) == '0')) {
+ ns.Multiply10 (1);
+ diff++;
+ }
+
+ if (!ns.RoundDecimal (info.DecimalDigits))
+ break;
+ }
+ }
+
+ int exp = diff >= 0 ? diff : -diff;
+ expPositive = -diff >= 0;
+ while (exp > 0) {
+ sb_exp.Insert (0, digitLowerTable [exp % 10]);
+ exp /= 10;
+ }
+ } else {
+ ns.RoundDecimal (info.DecimalDigits);
+ if (ns.ZeroOnly)
+ ns.Positive = true;
+ }
+
+ sb_int.Append (ns.GetIntegerString (ns.IntegerDigits));
+ if (sb_int.Length > info.IntegerDigits) {
+ int len = 0;
+ while (sb_int.Length > info.IntegerDigits && len < sb_int.Length) {
+ if (sb_int [len] == '0')
+ len ++;
+ else
+ break;
+ }
+ sb_int.Remove (0, len);
+ }
+
+ sb_dec.Append (ns.GetDecimalString (ns.DecimalDigits));
+
+ if (info.UseExponent) {
+ if (info.DecimalDigits <= 0 && info.IntegerDigits <= 0)
+ ns.Positive = true;
+
+ if (sb_int.Length < info.IntegerDigits)
+ sb_int.Insert (0, "0", info.IntegerDigits - sb_int.Length);
+
+ while (sb_exp.Length < info.ExponentDigits - info.ExponentTailSharpDigits)
+ sb_exp.Insert (0, "0");
+
+ if (expPositive && !info.ExponentNegativeSignOnly)
+ sb_exp.Insert (0, nfi.PositiveSign);
+ else if(!expPositive)
+ sb_exp.Insert (0, nfi.NegativeSign);
+ } else {
+ if (sb_int.Length < info.IntegerDigits - info.IntegerHeadSharpDigits)
+ sb_int.Insert (0, "0", info.IntegerDigits - info.IntegerHeadSharpDigits - sb_int.Length);
+ if (info.IntegerDigits == info.IntegerHeadSharpDigits && NumberStore.IsZeroOnly (sb_int))
+ sb_int.Remove (0, sb_int.Length);
+ }
+
+ ZeroTrimEnd (sb_dec, true);
+ while (sb_dec.Length < info.DecimalDigits - info.DecimalTailSharpDigits)
+ sb_dec.Append ('0');
+ if (sb_dec.Length > info.DecimalDigits)
+ sb_dec.Remove (info.DecimalDigits, sb_dec.Length - info.DecimalDigits);
+
+ return info.Format (format, nfi, ns.Positive, sb_int.ToString (), sb_dec.ToString (), sb_exp.ToString ());
+ }
+
+ private class CustomInfo
+ {
+ public bool UseGroup = false;
+ public int DecimalDigits = 0;
+ public int DecimalPointPos = -1;
+ public int DecimalTailSharpDigits = 0;
+ public int IntegerDigits = 0;
+ public int IntegerHeadSharpDigits = 0;
+ public int IntegerHeadPos = -1;
+ public bool UseExponent = false;
+ public int ExponentDigits = 0;
+ public int ExponentTailSharpDigits = 0;
+ public bool ExponentNegativeSignOnly = true;
+ public int DividePlaces = 0;
+ public int Percents = 0;
+ public int Permilles = 0;
+
+ public static string GetActiveSection (string format, ref bool positive, bool zero)
+ {
+ int[] lens = new int [3];
+ int index = 0;
+ int lastPos = 0;
+ char literal = '\0';
+ for (int i = 0; i < format.Length; i++) {
+ char c = format [i];
+
+ if (c == literal || (literal == '\0' && (c == '\"' || c == '\''))) {
+ if (literal == '\0')
+ literal = c;
+ else
+ literal = '\0';
+ continue;
+ }
+
+ if (literal == '\0' && format [i] == ';' && (i == 0 || format [i - 1] != '\\')) {
+ lens [index ++] = i - lastPos;
+ lastPos = i + 1;
+ if (index == 3)
+ break;
+ }
+ }
+
+ if (index == 0)
+ return format;
+ if (index == 1) {
+ if (positive || zero)
+ return format.Substring (0, lens [0]);
+ if (lens [0] + 1 < format.Length) {
+ positive = true;
+ return format.Substring (lens [0] + 1);
+ } else
+ return format.Substring (0, lens [0]);
+ }
+ if (index == 2) {
+ if (zero)
+ return format.Substring (lens [0] + lens [1] + 2);
+ if (positive)
+ return format.Substring (0, lens [0]);
+ if (lens [1] > 0) {
+ positive = true;
+ return format.Substring (lens [0] + 1, lens [1]);
+ } else
+ return format.Substring (0, lens [0]);
+ }
+ if (index == 3) {
+ if (zero)
+ return format.Substring (lens [0] + lens [1] + 2, lens [2]);
+ if (positive)
+ return format.Substring (0, lens [0]);
+ if (lens [1] > 0) {
+ positive = true;
+ return format.Substring (lens [0] + 1, lens [1]);
+ } else
+ return format.Substring (0, lens [0]);
+ }
+
+ throw new ArgumentException ();
+ }
+
+ public static CustomInfo Parse (string format, NumberFormatInfo nfi)
+ {
+ char literal = '\0';
+ bool integerArea = true;
+ bool decimalArea = false;
+ bool exponentArea = false;
+ bool sharpContinues = true;
+
+ CustomInfo info = new CustomInfo ();
+ int groupSeparatorCounter = 0;
+
+ for (int i = 0; i < format.Length; i++) {
+ char c = format [i];
+
+ if (c == literal && c != '\0') {
+ literal = '\0';
+ continue;
+ }
+ if (literal != '\0')
+ continue;
+
+ if (exponentArea && (c != '\0' && c != '0' && c != '#')) {
+ exponentArea = false;
+ integerArea = (info.DecimalPointPos < 0);
+ decimalArea = !integerArea;
+ i--;
+ continue;
+ }
+
+ switch (c) {
+ case '\\':
+ i ++;
+ continue;
+ case '\'':
+ case '\"':
+ if (c == '\"' || c == '\'') {
+ literal = c;
+ }
+ continue;
+ case '#':
+ if (sharpContinues && integerArea)
+ info.IntegerHeadSharpDigits ++;
+ else if (decimalArea)
+ info.DecimalTailSharpDigits ++;
+ else if (exponentArea)
+ info.ExponentTailSharpDigits ++;
+
+ goto case '0';
+ case '0':
+ if (c != '#') {
+ sharpContinues = false;
+ if (decimalArea)
+ info.DecimalTailSharpDigits = 0;
+ else if (exponentArea)
+ info.ExponentTailSharpDigits = 0;
+ }
+ if (info.IntegerHeadPos == -1)
+ info.IntegerHeadPos = i;
+
+ if (integerArea) {
+ info.IntegerDigits ++;
+ if (groupSeparatorCounter > 0)
+ info.UseGroup = true;
+ groupSeparatorCounter = 0;
+ } else if (decimalArea) {
+ info.DecimalDigits ++;
+ } else if (exponentArea) {
+ info.ExponentDigits ++;
+ }
+ break;
+ case 'e':
+ case 'E':
+ if (info.UseExponent)
+ break;
+
+ info.UseExponent = true;
+ integerArea = false;
+ decimalArea = false;
+ exponentArea = true;
+ if (i + 1 < format.Length) {
+ char nc = format [i + 1];
+ if (nc == '+')
+ info.ExponentNegativeSignOnly = false;
+ if (nc == '+' || nc == '-') {
+ i ++;
+ } else if (nc != '0' && nc != '#') {
+ info.UseExponent = false;
+ if (info.DecimalPointPos < 0)
+ integerArea = true;
+ }
+ c = '\0';
+ }
+
+ break;
+ case '.':
+ integerArea = false;
+ decimalArea = true;
+ exponentArea = false;
+ if (info.DecimalPointPos == -1)
+ info.DecimalPointPos = i;
+ break;
+ case '%':
+ info.Percents++;
+ break;
+ case '\u2030':
+ info.Permilles++;
+ break;
+ case ',':
+ if (integerArea && info.IntegerDigits > 0)
+ groupSeparatorCounter ++;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (info.ExponentDigits == 0)
+ info.UseExponent = false;
+ else
+ info.IntegerHeadSharpDigits = 0;
+
+ if (info.DecimalDigits == 0)
+ info.DecimalPointPos = -1;
+
+ info.DividePlaces += groupSeparatorCounter * 3;
+
+ return info;
+ }
+
+ public string Format (string format, NumberFormatInfo nfi, bool positive, string sb_int, string sb_dec, string sb_exp)
+ {
+ StringBuilder sb = new StringBuilder ();
+ char literal = '\0';
+ bool integerArea = true;
+ bool decimalArea = false;
+ int intSharpCounter = 0;
+ int sb_int_index = 0;
+ int sb_dec_index = 0;
+
+ int[] groups = GetFormattedGroupSizes (sb_int.Length, nfi);
+ int groupIndex = 0;
+ int groupSize = (groups.Length > 0 ? groups [groupIndex++] : -1);
+ int int_counter = 0;
+
+ for (int i = 0; i < format.Length; i++) {
+ char c = format [i];
+
+ if (c == literal && c != '\0') {
+ literal = '\0';
+ continue;
+ }
+ if (literal != '\0') {
+ sb.Append (c);
+ continue;
+ }
+
+ switch (c) {
+ case '\\':
+ i ++;
+ if (i < format.Length)
+ sb.Append (format [i]);
+ continue;
+ case '\'':
+ case '\"':
+ if (c == '\"' || c == '\'') {
+ literal = c;
+ }
+ continue;
+ case '#':
+ goto case '0';
+ case '0':
+ if (integerArea) {
+ intSharpCounter++;
+ if (IntegerDigits - intSharpCounter < sb_int.Length + sb_int_index || c == '0')
+ while (IntegerDigits - intSharpCounter + sb_int_index < sb_int.Length) {
+ sb.Append (sb_int[ sb_int_index++]);
+ int_counter ++;
+ if (UseGroup && groupSize > 0 && int_counter % groupSize == 0 && sb_int_index < sb_int.Length) {
+ sb.Append (nfi.NumberGroupSeparator);
+ groupSize = (groupIndex < groups.Length ? groups [groupIndex++] : groupSize);
+ int_counter = 0;
+ }
+ }
+ break;
+ } else if (decimalArea) {
+ if (sb_dec_index < sb_dec.Length)
+ sb.Append (sb_dec [sb_dec_index++]);
+ break;
+ }
+
+ sb.Append (c);
+ break;
+ case 'e':
+ case 'E':
+ if (sb_exp == null || !UseExponent) {
+ sb.Append (c);
+ break;
+ }
+
+ bool flag1 = true;
+ bool flag2 = false;
+
+ int q;
+ for (q = i + 1; q < format.Length; q++) {
+ if (format [q] == '0') {
+ flag2 = true;
+ continue;
+ }
+ if (q == i + 1 && (format [q] == '+' || format [q] == '-')) {
+ continue;
+ }
+ if (!flag2)
+ flag1 = false;
+ break;
+ }
+
+ if (flag1) {
+ i = q - 1;
+ integerArea = (DecimalPointPos < 0);
+ decimalArea = !integerArea;
+
+ sb.Append (c);
+ sb.Append (sb_exp);
+ sb_exp = null;
+ } else
+ sb.Append (c);
+
+ break;
+ case '.':
+ if (DecimalPointPos == i) {
+ if (DecimalDigits > 0) {
+ while (sb_int_index < sb_int.Length)
+ sb.Append (sb_int [sb_int_index++]);
+ }
+ if (sb_dec.Length > 0)
+ sb.Append (nfi.NumberDecimalSeparator);
+ }
+ integerArea = false;
+ decimalArea = true;
+ break;
+ case ',':
+ break;
+ case '%':
+ sb.Append (nfi.PercentSymbol);
+ break;
+ case '\u2030':
+ sb.Append (nfi.PerMilleSymbol);
+ break;
+ default:
+ sb.Append (c);
+ break;
+ }
+ }
+
+ if (!positive)
+ sb.Insert (0, nfi.NegativeSign);
+
+ return sb.ToString ();
+ }
+ private int[] GetFormattedGroupSizes (int intLen, NumberFormatInfo nfi)
+ {
+ int[] sizes = new int [intLen];
+
+ int index = 0;
+ int counter = 0;
+ int[] groups = nfi.NumberGroupSizes;
+ int groupIndex = 0;
+ int groupSize = (groups.Length > 0 ? groups [groupIndex++] : 0);
+ for (int i = 0; i < intLen; i++) {
+ counter ++;
+ if (groupSize > 0 && counter % groupSize == 0) {
+ sizes [index++] = groupSize;
+ groupSize = (groupIndex < groups.Length ? groups [groupIndex++] : groupSize);
+ counter = 0;
+ }
+ }
+
+ if (counter > 0) {
+ sizes [index++] = counter;
+ }
+
+ int[] temp = new int[index];
+ Array.Copy (sizes, 0, temp, 0, index);
+ Array.Reverse (temp);
+
+ return temp;
+ }
+ }
+
+ #endregion
+
+ #region Internal Class
+ internal class NumberStore
+ {
+ protected bool _NaN;
+ protected bool _infinity;
+ protected bool _positive;
+ protected int _decPointPos;
+ protected int _defPrecision;
+ protected int _defMaxPrecision;
+ protected int _defByteSize;
+
+ protected byte[] _digits;
+
+ #region Create
+ public static DoubleStore CreateInstance (double value)
+ {
+ return new DoubleStore (value);
+ }
+ public static SingleStore CreateInstance (float value)
+ {
+ return new SingleStore (value);
+ }
+ public static IntegerStore CreateInstance (long value)
+ {
+ return new IntegerStore (value);
+ }
+ public static IntegerStore CreateInstance (ulong value)
+ {
+ return new IntegerStore (value);
+ }
+ public static IntegerStore CreateInstance (int value)
+ {
+ return new IntegerStore (value);
+ }
+ public static IntegerStore CreateInstance (uint value)
+ {
+ return new IntegerStore (value);
+ }
+ public static IntegerStore CreateInstance (short value)
+ {
+ return new IntegerStore (value);
+ }
+ public static IntegerStore CreateInstance (ushort value)
+ {
+ return new IntegerStore (value);
+ }
+ public static IntegerStore CreateInstance (byte value)
+ {
+ return new IntegerStore (value);
+ }
+ public static IntegerStore CreateInstance (sbyte value)
+ {
+ return new IntegerStore (value);
+ }
+ #endregion
+
+ #region Public Property
+ public virtual bool IsNaN
+ {
+ get { return _NaN; }
+ }
+ public virtual bool IsInfinity {
+ get { return _infinity; }
+ }
+ public virtual int DecimalPointPosition {
+ get { return _decPointPos; }
+ }
+ public virtual bool Positive {
+ get { return _positive; }
+ set { _positive = value;}
+ }
+ public virtual int DefaultPrecision {
+ get { return _defPrecision; }
+ }
+ public virtual int DefaultMaxPrecision {
+ get { return _defMaxPrecision; }
+ }
+ public virtual int DefaultByteSize {
+ get { return _defByteSize; }
+ }
+ public virtual bool HasDecimal {
+ get { return _digits.Length > _decPointPos; }
+ }
+ public virtual int IntegerDigits {
+ get { return _decPointPos > 0 ? _decPointPos : 1; }
+ }
+ public virtual int DecimalDigits {
+ get { return HasDecimal ? _digits.Length - _decPointPos : 0; }
+ }
+ public virtual bool IsFloatingSource {
+ get { return _defPrecision == 15 || _defPrecision == 7; }
+ }
+ public virtual bool ZeroOnly {
+ get {
+ for (int i = 0; i < _digits.Length; i++)
+ if (_digits [i] != 0)
+ return false;
+ return true;
+ }
+ }
+ #endregion
+
+ #region Public Method
+ public virtual bool RoundPos (int pos)
+ {
+ return RoundPos (pos, true);
+ }
+ public virtual bool RoundPos (int pos, bool carryFive)
+ {
+ bool carry = false;
+
+ if (_decPointPos <= 0)
+ pos = pos - _decPointPos - 1;
+
+ if (pos >= _digits.Length)
+ return false;
+
+ if (pos < 0) {
+ _digits = new byte [1];
+ _digits [0] = 0;
+ _decPointPos = 1;
+ _positive = true;
+ return false;
+ }
+
+ for (int i = pos; i >= 0; i--) {
+ RoundHelper (i, carryFive, ref carry);
+ if (!carry)
+ break;
+ }
+
+ if (carry) {
+ byte[] temp = new byte [_digits.Length + 1];
+ _digits.CopyTo (temp, 1);
+ temp [0] = 1;
+ _digits = temp;
+ _decPointPos ++;
+ pos ++;
+ }
+
+ for (int i = pos; i < _digits.Length; i++)
+ _digits [i] = 0;
+ TrimDecimalEndZeros ();
+
+ return carry;
+ }
+ public virtual bool RoundDecimal (int decimals)
+ {
+ return RoundDecimal (decimals, true);
+ }
+ public virtual bool RoundDecimal (int decimals, bool carryFive)
+ {
+ bool carry = false;
+
+ decimals += _decPointPos;
+
+ if (!HasDecimal || decimals >= _digits.Length)
+ return false;
+
+ if (decimals < 0) {
+ _digits = new byte [1];
+ _digits [0] = 0;
+ _decPointPos = 1;
+ _positive = true;
+ return false;
+ }
+
+ for (int i = decimals; i >= 0; i--) {
+ RoundHelper (i, carryFive, ref carry);
+ if (!carry)
+ break;
+ }
+
+ if (carry) {
+ byte[] temp = new byte [_digits.Length + 1];
+ _digits.CopyTo (temp, 1);
+ temp [0] = 1;
+ _digits = temp;
+ _decPointPos ++;
+ decimals ++;
+ }
+
+ for (int i = decimals; i < _digits.Length; i++)
+ _digits [i] = 0;
+ TrimDecimalEndZeros ();
+
+ return carry;
+ }
+ protected virtual void RoundHelper (int index, bool carryFive, ref bool carry)
+ {
+ if (carry) {
+ if (_digits [index] == 9) {
+ carry = true;
+ _digits [index] = 0;
+ } else {
+ carry = false;
+ _digits [index] ++;
+ }
+ } else if (_digits [index] >= (carryFive ? 5 : 6)) {
+ carry = true;
+ }
+ }
+ public virtual bool RoundEffectiveDigits (int digits)
+ {
+ return RoundEffectiveDigits (digits, true, true);
+ }
+ public virtual bool RoundEffectiveDigits (int digits, bool carryFive, bool carryEven)
+ {
+ bool carry = false;
+
+ if (digits >= _digits.Length || digits < 0)
+ return false;
+
+ if (digits + 1 < _digits.Length && _digits [digits + 1] == 5 && _digits [digits] % 2 == (carryEven ? 0 : 1))
+ carryFive = false;
+
+ for (int i = digits; i >= 0; i--) {
+ RoundHelper (i, carryFive, ref carry);
+ if (!carry)
+ break;
+ }
+
+ if (carry) {
+ byte[] temp = new byte [_digits.Length + 1];
+ _digits.CopyTo (temp, 1);
+ temp [0] = 1;
+ _digits = temp;
+ _decPointPos ++;
+ digits ++;
+ }
+
+ for (int i = digits; i < _digits.Length; i++)
+ _digits [i] = 0;
+ TrimDecimalEndZeros ();
+
+ return carry;
+ }
+ public virtual void TrimDecimalEndZeros ()
+ {
+ int len = 0;
+ for (int i = _digits.Length - 1; i >= 0; i --) {
+ if (_digits [i] != 0)
+ break;
+ len ++;
+ }
+
+ if (len > 0) {
+ byte[] temp = new byte [_digits.Length - len];
+ Array.Copy (_digits, 0, temp, 0, temp.Length);
+ _digits = temp;
+ }
+ }
+ public virtual void TrimIntegerStartZeros ()
+ {
+ if (_decPointPos < 0 && _decPointPos >= _digits.Length)
+ return;
+
+ int len = 0;
+ for (int i = 0; i < _decPointPos && i < _digits.Length; i++) {
+ if (_digits [i] != 0)
+ break;
+ len ++;
+ }
+
+ if (len == _decPointPos)
+ len --;
+
+ if (len == _digits.Length) {
+ _digits = new byte [1];
+ _digits [0] = 0;
+ _decPointPos = 1;
+ _positive = true;
+ } else if (len > 0) {
+ byte[] temp = new byte [_digits.Length - len];
+ Array.Copy (_digits, len, temp, 0, temp.Length);
+ _digits = temp;
+ _decPointPos -= len;
+ }
+ }
+
+ public virtual string GetIntegerString (int minLength)
+ {
+ if (IntegerDigits == 0)
+ return new string ('0', minLength);
+
+ StringBuilder sb = new StringBuilder (IntegerDigits > minLength ? IntegerDigits : minLength);
+ for (int i = 0; i < _decPointPos; i++) {
+ if (i < _digits.Length)
+ sb.Append ((char)('0' + _digits [i]));
+ else
+ sb.Append ('0');
+ }
+ if (sb.Length < minLength)
+ sb.Insert (0, "0", minLength - sb.Length);
+ return sb.ToString ();
+ }
+ public virtual string GetDecimalString (int precision)
+ {
+ if (!HasDecimal)
+ return new string ('0', precision);
+
+ StringBuilder sb = new StringBuilder (precision);
+ for (int i = _decPointPos; i < _digits.Length && i < precision + _decPointPos; i++) {
+ if (i >= 0)
+ sb.Append ((char)('0' + _digits [i]));
+ else
+ sb.Append ('0');
+ }
+ if (sb.Length < precision)
+ sb.Append ('0', precision - sb.Length);
+ else if (sb.Length > precision)
+ sb.Remove (0, precision);
+ return sb.ToString ();
+ }
+ public virtual void Multiply10 (int count)
+ {
+ if (count <= 0)
+ return;
+
+ _decPointPos += count;
+
+ TrimIntegerStartZeros ();
+ }
+ public virtual void Divide10 (int count)
+ {
+ if (count <= 0)
+ return;
+
+ _decPointPos -= count;
+ }
+ public override string ToString()
+ {
+ StringBuilder sb = new StringBuilder ();
+ sb.Append (GetIntegerString (IntegerDigits));
+ if (HasDecimal) {
+ sb.Append (".");
+ sb.Append (GetDecimalString (DecimalDigits));
+ }
+ return sb.ToString ();
+ }
+ public virtual char GetChar (int pos)
+ {
+ if (_decPointPos <= 0)
+ pos += _decPointPos - 1;
+
+ if (pos < 0 || pos >= _digits.Length)
+ return '0';
+ else
+ return (char)('0' + _digits [pos]);
+ }
+ public virtual NumberStore GetClone ()
+ {
+ NumberStore ns = new NumberStore ();
+
+ ns._decPointPos = this._decPointPos;
+ ns._defMaxPrecision = this._defMaxPrecision;
+ ns._defPrecision = this._defPrecision;
+ ns._digits = (byte[])this._digits.Clone ();
+ ns._infinity = this._infinity;
+ ns._NaN = this._NaN;
+ ns._positive = this._positive;
+
+ return ns;
+ }
+ #endregion
+
+ #region Public Static Method
+ public static bool IsZeroOnly (StringBuilder sb)
+ {
+ for (int i = 0; i < sb.Length; i++)
+ if (char.IsDigit (sb [i]) && sb [i] != '0')
+ return false;
+ return true;
+ }
+ #endregion
+ }
+
+ internal class IntegerStore : NumberStore
+ {
+ public IntegerStore (long value)
+ {
+ _positive = value >= 0;
+ ulong v = (ulong)(_positive ? value : -value);
+
+ byte[] temp = new byte [30];
+ int i = temp.Length - 1;
+
+ while (v > 0) {
+ temp [i--] = (byte)(v % 10);
+ v /= 10;
+ }
+
+ if (temp.Length - i - 1 > 0) {
+ _digits = new byte [temp.Length - i - 1];
+ Array.Copy (temp, i + 1, _digits, 0, _digits.Length);
+ } else {
+ _digits = new byte [1];
+ }
+
+ _defByteSize = 8;
+ _defMaxPrecision = _defPrecision = 19;
+ _decPointPos = _digits.Length;
+ }
+ public IntegerStore (int value) : this ((long)value)
+ {
+ _defByteSize = 4;
+ _defMaxPrecision = _defPrecision = 10;
+ }
+ public IntegerStore (short value) : this ((long)value)
+ {
+ _defByteSize = 2;
+ _defMaxPrecision = _defPrecision = 5;
+ }
+ public IntegerStore (sbyte value) : this ((long)value)
+ {
+ _defByteSize = 1;
+ _defMaxPrecision = _defPrecision = 3;
+ }
+
+ public IntegerStore (ulong value)
+ {
+ _positive = true;
+
+ byte[] temp = new byte [30];
+ int i = temp.Length - 1;
+
+ while (value > 0) {
+ temp [i--] = (byte)(value % 10);
+ value /= 10;
+ }
+
+ if (temp.Length - i - 1 > 0) {
+ _digits = new byte [temp.Length - i - 1];
+ Array.Copy (temp, i + 1, _digits, 0, _digits.Length);
+ } else {
+ _digits = new byte [1];
+ }
+
+ _defByteSize = 8;
+ _defMaxPrecision = _defPrecision = 20;
+ _decPointPos = _digits.Length;
+ }
+ public IntegerStore (uint value) : this ((ulong)value)
+ {
+ _defByteSize = 4;
+ _defMaxPrecision = _defPrecision = 10;
+ }
+ public IntegerStore (ushort value) : this ((ulong)value)
+ {
+ _defByteSize = 2;
+ _defMaxPrecision = _defPrecision = 5;
+ }
+ public IntegerStore (byte value) : this ((ulong)value)
+ {
+ _defByteSize = 1;
+ _defMaxPrecision = _defPrecision = 3;
+ }
+ }
+ internal class DoubleStore : NumberStore
+ {
+ public DoubleStore(double value)
+ {
+ _defPrecision = 15;
+ _defMaxPrecision = _defPrecision + 2;
+
+ if (double.IsNaN (value) || double.IsInfinity (value)) {
+ _NaN = double.IsNaN (value);
+ _infinity = double.IsInfinity (value);
+ _positive = value > 0;
+ return;
+ }
+
+ long bits = BitConverter.DoubleToInt64Bits (value);
+ _positive = (bits >= 0);
+ int e = (int) ((bits >> 52) & 0x7ffL);
+ long m = bits & 0xfffffffffffffL;
+
+ if (e == 0 && m == 0) {
+ _decPointPos = 1;
+ _digits = new byte []{0};
+ _positive = true;
+ return;
+ }
+
+ bool flag = true;
+ if (e == 0) {
+ flag = false;
+ e ++;
+ } else if (e != 0) {
+ m |= (1L << 52);
+ }
+
+ e -= 1075;
+
+ int nsize = 0;
+ while ((m & 1) == 0) {
+ m >>= 1;
+ e ++;
+ nsize ++;
+ }
+
+ long mt = m;
+ int length = 1;
+ byte[] temp = new byte [56];
+ for (int i = temp.Length - 1; i >= 0; i--, length++) {
+ temp [i] = (byte)(mt % 10);
+ mt /= 10;
+ if (mt == 0)
+ break;
+ }
+
+ _decPointPos = temp.Length - 1;
+
+ if (e >= 0) {
+ for (int i = 0; i < e; i++) {
+ if (MultiplyBy (ref temp, ref length, 2)) {
+ _decPointPos ++;
+ }
+ }
+ } else {
+ for (int i = 0; i < -e; i++) {
+ if (MultiplyBy (ref temp, ref length, 5)) {
+ _decPointPos ++;
+ }
+ }
+ _decPointPos += e;
+ }
+
+ int ulvc = 1;
+ ulong ulv = 0;
+ for (int i = 0; i < temp.Length; i++)
+ if (temp [i] != 0) {
+ _decPointPos -= i - 1;
+ _digits = new byte [temp.Length - i];
+ for (int q = i; q < temp.Length; q++) {
+ _digits [q - i] = temp [q];
+ if (ulvc < 20) {
+ ulv = ulv * 10 + temp [q];
+ ulvc ++;
+ }
+ }
+ break;
+ }
+
+ RoundEffectiveDigits (17, true, true);
+ }
+
+ internal bool MultiplyBy (ref byte[] buffer,ref int length, int amount)
+ {
+ int mod = 0;
+ int ret;
+ int start = buffer.Length - length - 1;
+ if (start < 0) start = 0;
+
+ for (int i = buffer.Length - 1; i > start; i--) {
+ ret = buffer [i] * amount + mod;
+ mod = ret / 10;
+ buffer [i] = (byte)(ret % 10);
+ }
+
+ if (mod != 0) {
+ length = buffer.Length - start;
+
+ if (start == 0) {
+ buffer [0] = (byte)mod;
+ Array.Copy (buffer, 0, buffer, 1, buffer.Length - 1);
+ buffer [0] = 0;
+ return true;
+ }
+ else {
+ buffer [start] = (byte)mod;
+ }
+ }
+
+ return false;
+ }
+ }
+
+ internal class SingleStore : NumberStore
+ {
+ public SingleStore(float value)
+ {
+ _defPrecision = 7;
+ _defMaxPrecision = _defPrecision + 2;
+
+ if (float.IsNaN (value) || float.IsInfinity (value))
+ throw new ArgumentException ();
+
+ long bits = BitConverter.DoubleToInt64Bits (value);
+ _positive = (bits >= 0);
+ int e = (int) ((bits >> 52) & 0x7ffL);
+ long m = bits & 0xfffffffffffffL;
+
+ if (e == 0 && m == 0) {
+ _decPointPos = 1;
+ _digits = new byte []{0};
+ _positive = true;
+ return;
+ }
+
+ if (e == 0) {
+ e ++;
+ } else if (e != 0) {
+ m |= (1L << 52);
+ }
+
+ e -= 1075;
+
+ int nsize = 0;
+ while ((m & 1) == 0) {
+ m >>= 1;
+ e ++;
+ nsize ++;
+ }
+
+ long mt = m;
+ int length = 1;
+ byte[] temp = new byte [26];
+ for (int i = temp.Length - 1; i >= 0; i--, length++) {
+ temp [i] = (byte)(mt % 10);
+ mt /= 10;
+ if (mt == 0)
+ break;
+ }
+
+ _decPointPos = temp.Length - 1;
+
+ if (e >= 0) {
+ for (int i = 0; i < e; i++) {
+ if (MultiplyBy (ref temp, ref length, 2)) {
+ _decPointPos ++;
+ }
+ }
+ } else {
+ for (int i = 0; i < -e; i++) {
+ if (MultiplyBy (ref temp, ref length, 5)) {
+ _decPointPos ++;
+ }
+ }
+ _decPointPos += e;
+ }
+
+ int ulvc = 1;
+ ulong ulv = 0;
+ for (int i = 0; i < temp.Length; i++)
+ if (temp [i] != 0) {
+ _decPointPos -= i - 1;
+ _digits = new byte [temp.Length - i];
+ for (int q = i; q < temp.Length; q++) {
+ _digits [q - i] = temp [q];
+ if (ulvc < 20) {
+ ulv = ulv * 10 + temp [q];
+ ulvc ++;
+ }
+ }
+ break;
+ }
+
+ RoundEffectiveDigits (9, true, true);
+ }
+ internal bool MultiplyBy (ref byte[] buffer,ref int length, int amount)
+ {
+ int mod = 0;
+ int ret;
+ int start = buffer.Length - length - 1;
+ if (start < 0) start = 0;
+
+ for (int i = buffer.Length - 1; i > start; i--) {
+ ret = buffer [i] * amount + mod;
+ mod = ret / 10;
+ buffer [i] = (byte)(ret % 10);
+ }
+
+ if (mod != 0) {
+ length = buffer.Length - start;
+
+ if (start == 0) {
+ buffer [0] = (byte)mod;
+ Array.Copy (buffer, 0, buffer, 1, buffer.Length - 1);
+ buffer [0] = 0;
+ return true;
+ }
+ else {
+ buffer [start] = (byte)mod;
+ }
+ }
+
+ return false;
+ }
+ }
+
+ #endregion
+ }
+
+ #region Wrapper
+ class IntegerFormatter
+ {
+ #region NumberToString
+ public static string NumberToString (string format, NumberFormatInfo nfi, short value)
+ {
+ return NumberFormatter.NumberToString (format, value, nfi);
+ }
+
+ public static string NumberToString (string format, NumberFormatInfo nfi, int value)
+ {
+ return NumberFormatter.NumberToString (format, value, nfi);
+ }
+
+ public static string NumberToString (string format, NumberFormatInfo nfi, long value)
+ {
+ return NumberFormatter.NumberToString (format, value, nfi);
+ }
+
+ public static string NumberToString (string format, NumberFormatInfo nfi, sbyte value)
+ {
+ return NumberFormatter.NumberToString (format, value, nfi);
+ }
+
+ public static string NumberToString (string format, NumberFormatInfo nfi, byte value)
+ {
+ return NumberFormatter.NumberToString (format, value, nfi);
+ }
+
+ public static string NumberToString (string format, NumberFormatInfo nfi, ushort value)
+ {
+ return NumberFormatter.NumberToString (format, value, nfi);
+ }
+
+ public static string NumberToString (string format, NumberFormatInfo nfi, uint value)
+ {
+ return NumberFormatter.NumberToString (format, value, nfi);
+ }
+
+ public static string NumberToString (string format, NumberFormatInfo nfi, ulong value)
+ {
+ return NumberFormatter.NumberToString (format, value, nfi);
+ }
+ #endregion
+
+ #region Wrapper
+ internal static string FormatDecimal (long value, int precision, int maxLength)
+ {
+ return NumberFormatter.FormatDecimal (NumberFormatter.NumberStore.CreateInstance (value), precision, System.Globalization.CultureInfo.CurrentCulture.NumberFormat);
+ }
+ internal static string FormatGeneral (long value, int precision, NumberFormatInfo nfi, bool upper)
+ {
+ return NumberFormatter.FormatGeneral (NumberFormatter.NumberStore.CreateInstance (value), precision, System.Globalization.CultureInfo.CurrentCulture.NumberFormat, upper);
+ }
+
+ internal static string FormatGeneral (long value, int precision, NumberFormatInfo nfi, bool upper, int maxLength)
+ {
+ return NumberFormatter.FormatGeneral (NumberFormatter.NumberStore.CreateInstance (value), precision, System.Globalization.CultureInfo.CurrentCulture.NumberFormat, upper);
+ }
+ #endregion
+ }
+
+ class FloatingPointFormatter
+ {
+ public FloatingPointFormatter (double p, double p10, int dec_len, int dec_len_min, double p2, double p102, int dec_len2, int dec_len_min2)
+ {
+
+ }
+
+ public string GetStringFrom (string format, NumberFormatInfo nfi, float value)
+ {
+ if (nfi == null) nfi = CultureInfo.CurrentCulture.NumberFormat;
+ return NumberFormatter.NumberToString (format, value, nfi);
+ }
+
+ public string GetStringFrom (string format, NumberFormatInfo nfi, double value)
+ {
+ if (nfi == null) nfi = CultureInfo.CurrentCulture.NumberFormat;
+ return NumberFormatter.NumberToString (format, value, nfi);
+ }
+ }
+ #endregion
+}
\ No newline at end of file
const int dec_len2 = 7;
const int dec_len_min2 = -16;
- public static string NumberToString (string format, NumberFormatInfo nfi, double value)
+ public static string NumberToString (string format, NumberFormatInfo nfi, float value)
{
if (fpf == null) {
fpf = new FloatingPointFormatter (p, p10, dec_len, dec_len_min, p2, p102, dec_len2, dec_len_min2);
System/ExecutionEngineException.cs
System/FieldAccessException.cs
System/FlagsAttribute.cs
-System/FloatingPointFormatter.cs
System/FormatException.cs
System/GC.cs
System/GenericParameterAttributes.cs
System/Int16.cs
System/Int32.cs
System/Int64.cs
-System/IntegerFormatter.cs
System/IntPtr.cs
System/InvalidCastException.cs
System/InvalidOperationException.cs
System/NotSupportedException.cs
System/Nullable.cs
System/NullReferenceException.cs
+System/NumberFormatter.cs
System/Object.cs
System/ObjectDisposedException.cs
System/ObsoleteAttribute.cs