// // 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) { char specifier; int precision; bool custom; ParseBasicFormat (format, out specifier, out precision, out custom); if (!custom && (specifier == 'x' || specifier == 'X')) return FormatHexadecimal (value >= 0 ? (ulong)value : (ulong)-value, value >= 0, 1, precision, specifier == 'X'); return NumberToString (format, new NumberStore (value), nfi, specifier, precision, custom); } public static string NumberToString (string format, byte value, NumberFormatInfo nfi) { char specifier; int precision; bool custom; ParseBasicFormat (format, out specifier, out precision, out custom); if (!custom && (specifier == 'x' || specifier == 'X')) return FormatHexadecimal (value, true, 1, precision, specifier == 'X'); return NumberToString (format, new NumberStore (value), nfi, specifier, precision, custom); } public static string NumberToString (string format, ushort value, NumberFormatInfo nfi) { char specifier; int precision; bool custom; ParseBasicFormat (format, out specifier, out precision, out custom); if (!custom && (specifier == 'x' || specifier == 'X')) return FormatHexadecimal (value, true, 2, precision, specifier == 'X'); return NumberToString (format, new NumberStore (value), nfi, specifier, precision, custom); } public static string NumberToString (string format, short value, NumberFormatInfo nfi) { char specifier; int precision; bool custom; ParseBasicFormat (format, out specifier, out precision, out custom); if (!custom && (specifier == 'x' || specifier == 'X')) return FormatHexadecimal (value >= 0 ? (ulong)value : (ulong)-value, value >= 0, 2, precision, specifier == 'X'); return NumberToString (format, new NumberStore (value), nfi, specifier, precision, custom); } public static string NumberToString (string format, uint value, NumberFormatInfo nfi) { char specifier; int precision; bool custom; ParseBasicFormat (format, out specifier, out precision, out custom); if (!custom && (specifier == 'x' || specifier == 'X')) return FormatHexadecimal (value, true, 4, precision, specifier == 'X'); return NumberToString (format, new NumberStore (value), nfi, specifier, precision, custom); } public static string NumberToString (string format, int value, NumberFormatInfo nfi) { char specifier; int precision; bool custom; ParseBasicFormat (format, out specifier, out precision, out custom); if (!custom && (specifier == 'x' || specifier == 'X')) return FormatHexadecimal (value >= 0 ? (ulong)value : (ulong)-(long)value, value >= 0, 4, precision, specifier == 'X'); return NumberToString (format, new NumberStore (value), nfi, specifier, precision, custom); } public static string NumberToString (string format, ulong value, NumberFormatInfo nfi) { char specifier; int precision; bool custom; ParseBasicFormat (format, out specifier, out precision, out custom); if (!custom && (specifier == 'x' || specifier == 'X')) return FormatHexadecimal (value, true, 8, precision, specifier == 'X'); return NumberToString (format, new NumberStore (value), nfi, specifier, precision, custom); } public static string NumberToString (string format, long value, NumberFormatInfo nfi) { char specifier; int precision; bool custom; ParseBasicFormat (format, out specifier, out precision, out custom); if (!custom && (specifier == 'x' || specifier == 'X')) return FormatHexadecimal (value >= 0 ? (ulong)value : (ulong)-value, value >= 0, 8, precision, specifier == 'X'); return NumberToString (format, new NumberStore (value), nfi, specifier, precision, custom); } public static string NumberToString (string format, float value, NumberFormatInfo nfi) { char specifier; int precision; bool custom; ParseBasicFormat (format, out specifier, out precision, out custom); if (!custom && (specifier == 'x' || specifier == 'X')) throw new FormatException (); return NumberToString (format, new NumberStore (value), nfi, specifier, precision, custom); } public static string NumberToString (string format, double value, NumberFormatInfo nfi) { char specifier; int precision; bool custom; ParseBasicFormat (format, out specifier, out precision, out custom); if (!custom && (specifier == 'x' || specifier == 'X')) throw new FormatException (); return NumberToString (format, new NumberStore (value), nfi, specifier, precision, custom); } public static string NumberToString (string format, decimal value, NumberFormatInfo nfi) { char specifier; int precision; bool custom; ParseBasicFormat (format, out specifier, out precision, out custom); if (!custom && (specifier == 'x' || specifier == 'X')) throw new FormatException (); return NumberToString (format, new NumberStore (value), nfi, specifier, precision, custom); } public static string NumberToString (string format, NumberStore ns, NumberFormatInfo nfi, char specifier, int precision, bool custom) { if (ns.IsNaN) { return nfi.NaNSymbol; } if (ns.IsInfinity) { if (ns.Positive) return nfi.PositiveInfinitySymbol; else return nfi.NegativeInfinitySymbol; } if (nfi == null) nfi = NumberFormatInfo.GetInstance (null); 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, ns.IsBankerApplicable, true); 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': if (ns.IsFloatingSource || ns.IsDecimalSource || precision != -1) return FormatGeneral (ns, precision, nfi, specifier == 'G', false); return FormatDecimal (ns, precision, nfi); 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.DefaultPrecision, nfi, true, true); } else { throw new FormatException (Locale.GetText ("The specified format cannot be used in this instance")); } default: throw new FormatException (Locale.GetText ("The specified format '" + format + "' is invalid")); } } #endregion #region BasicParser private static void ParseBasicFormat (string format, out char specifier, out int precision, out bool custom) { if (format == null || format.Length == 0) { precision = -1; specifier = 'G'; custom = false; return; } precision = -1; specifier = format[0]; custom = false; if (Char.IsLetter (specifier)) { if (format.Length == 1) return; 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; } custom = true; return; } #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) { precision = (precision >= 0 ? precision : nfi.CurrencyDecimalDigits); ns.RoundDecimal (precision); StringBuilder sb = new StringBuilder (ns.IntegerDigits * 2 + precision * 2 + 16); bool needNegativeSign = !ns.Positive && !ns.ZeroOnly; if (!needNegativeSign) { switch (nfi.CurrencyPositivePattern) { case 0: sb.Append (nfi.CurrencySymbol); break; case 2: sb.Append (nfi.CurrencySymbol); sb.Append (' '); break; } } else { switch (nfi.CurrencyNegativePattern) { case 0: sb.Append ('('); sb.Append (nfi.CurrencySymbol); break; case 1: sb.Append (nfi.NegativeSign); sb.Append (nfi.CurrencySymbol); break; case 2: sb.Append (nfi.CurrencySymbol); sb.Append (nfi.NegativeSign); break; case 3: sb.Append (nfi.CurrencySymbol); break; case 4: sb.Append ('('); break; case 5: sb.Append (nfi.NegativeSign); break; case 8: sb.Append (nfi.NegativeSign); break; case 9: sb.Append (nfi.NegativeSign); sb.Append (nfi.CurrencySymbol); sb.Append (' '); break; case 11: sb.Append (nfi.CurrencySymbol); sb.Append (' '); break; case 12: sb.Append (nfi.CurrencySymbol); sb.Append (' '); sb.Append (nfi.NegativeSign); break; case 14: sb.Append ('('); sb.Append (nfi.CurrencySymbol); sb.Append (' '); break; case 15: sb.Append ('('); break; } } ns.AppendIntegerStringWithGroupSeparator (sb, nfi.CurrencyGroupSizes, nfi.CurrencyGroupSeparator); if (precision > 0) { sb.Append (nfi.CurrencyDecimalSeparator); ns.AppendDecimalString (precision, sb); } if (!needNegativeSign) { switch (nfi.CurrencyPositivePattern) { case 1: sb.Append (nfi.CurrencySymbol); break; case 3: sb.Append (' '); sb.Append (nfi.CurrencySymbol); break; } } else { switch (nfi.CurrencyNegativePattern) { case 0: sb.Append (')'); break; case 3: sb.Append (nfi.NegativeSign); break; case 4: sb.Append (nfi.CurrencySymbol); sb.Append (')'); break; case 5: 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.Append (' '); sb.Append (nfi.CurrencySymbol); break; case 10: sb.Append (' '); sb.Append (nfi.CurrencySymbol); sb.Append (nfi.NegativeSign); break; case 11: sb.Append (nfi.NegativeSign); break; case 13: sb.Append (nfi.NegativeSign); sb.Append (' '); sb.Append (nfi.CurrencySymbol); break; case 14: sb.Append (')'); break; case 15: 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 || ns.IsDecimalSource) throw new FormatException (); precision = precision > 0 ? precision : 1; precision = ns.IntegerDigits > precision ? ns.IntegerDigits : precision; StringBuilder sb = new StringBuilder (precision + nfi.NegativeSign.Length); if (!ns.Positive && !ns.CheckZeroOnlyInteger ()) { sb.Append (nfi.NegativeSign); } ns.AppendIntegerString (precision, sb); return sb.ToString (); } internal static string FormatFixedPoint (NumberStore ns, int precision, NumberFormatInfo nfi) { precision = precision >= 0 ? precision : nfi.NumberDecimalDigits; ns.RoundDecimal (precision); StringBuilder cb = new StringBuilder (ns.IntegerDigits + precision + nfi.NumberDecimalSeparator.Length); if (!ns.Positive && !ns.ZeroOnly) cb.Append (nfi.NegativeSign); ns.AppendIntegerString (ns.IntegerDigits > 0 ? ns.IntegerDigits : 1, cb); if (precision > 0) { cb.Append (nfi.NumberDecimalSeparator); ns.AppendDecimalString (precision, cb); } return cb.ToString (); } internal static string FormatGeneral (NumberStore ns) { return FormatGeneral (ns, -1, NumberFormatInfo.CurrentInfo, true, false); } internal static string FormatGeneral (NumberStore ns, IFormatProvider provider) { return FormatGeneral (ns, -1, NumberFormatInfo.GetInstance (provider), true, false); } private static string FormatGeneral (NumberStore ns, int precision, NumberFormatInfo nfi, bool upper, bool roundtrip) { if (ns.ZeroOnly) return "0"; precision = precision > 0 ? precision : ns.DefaultPrecision; int exponent = 0; bool expMode = (ns.IsDecimalSource && precision == ns.DefaultPrecision ? false : (ns.IntegerDigits > precision || ns.DecimalPointPosition <= -4)); if (expMode) { while (!(ns.DecimalPointPosition == 1 && ns.GetChar (0) != '0')) { if (ns.DecimalPointPosition > 1) { ns.Divide10 (1); exponent ++; } else { ns.Multiply10 (1); exponent --; } } } precision = precision < ns.DefaultPrecision + 2 ? (precision < ns.DefaultMaxPrecision ? precision : ns.DefaultMaxPrecision) : ns.DefaultPrecision + 2; StringBuilder cb = new StringBuilder (ns.IntegerDigits + precision + 16); if (expMode) { if (ns.RoundDecimal (precision - 1)) { ns.Divide10 (1); exponent ++; } } else if (!roundtrip) { if (ns.IsDecimalSource) ns.RoundPos (precision); else ns.RoundDecimal (precision, true, false); } if (!ns.Positive) { cb.Append (nfi.NegativeSign); } ns.AppendIntegerString (ns.IntegerDigits > 0 ? ns.IntegerDigits : 1, cb); if (ns.DecimalDigits > 0) { cb.Append (nfi.NumberDecimalSeparator); ns.AppendDecimalString (ns.DecimalDigits, cb); } if (expMode) { if (upper) cb.Append ('E'); else cb.Append ('e'); if (exponent >= 0) cb.Append (nfi.PositiveSign); else { cb.Append (nfi.NegativeSign); exponent = -exponent; } if (exponent == 0) { cb.Append ('0', 2); } else if (exponent < 10) { cb.Append ('0'); cb.Append (digitLowerTable [exponent]); } else if (exponent < 100) { cb.Append (digitLowerTable [exponent / 10 % 10]); cb.Append (digitLowerTable [exponent % 10]); } else if (exponent < 1000) { cb.Append (digitLowerTable [exponent / 100 % 10]); cb.Append (digitLowerTable [exponent / 10 % 10]); cb.Append (digitLowerTable [exponent % 10]); } } return cb.ToString (); } internal static string FormatNumber (NumberStore ns, int precision, NumberFormatInfo nfi) { precision = (precision >= 0 ? precision : nfi.NumberDecimalDigits); StringBuilder sb = new StringBuilder(ns.IntegerDigits * 3 + precision); ns.RoundDecimal (precision); bool needNegativeSign = (!ns.Positive && !ns.ZeroOnly); if (needNegativeSign) { switch (nfi.NumberNegativePattern) { case 0: sb.Append ('('); break; case 1: sb.Append (nfi.NegativeSign); break; case 2: sb.Append (nfi.NegativeSign); sb.Append (' '); break; } } ns.AppendIntegerStringWithGroupSeparator (sb, nfi.NumberGroupSizes, nfi.NumberGroupSeparator); if (precision > 0) { sb.Append (nfi.NumberDecimalSeparator); ns.AppendDecimalString (precision, sb); } if (needNegativeSign) { switch (nfi.NumberNegativePattern) { case 0: sb.Append (')'); 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) { precision = (precision >= 0 ? precision : nfi.PercentDecimalDigits); ns.Multiply10 (2); ns.RoundDecimal (precision); bool needNegativeSign = (!ns.Positive && !ns.ZeroOnly); StringBuilder sb = new StringBuilder(ns.IntegerDigits * 2 + precision + 16); if (!needNegativeSign) { if (nfi.PercentPositivePattern == 2) { sb.Append (nfi.PercentSymbol); } } else { switch (nfi.PercentNegativePattern) { case 0: sb.Append (nfi.NegativeSign); break; case 1: sb.Append (nfi.NegativeSign); break; case 2: sb.Append (nfi.NegativeSign); sb.Append (nfi.PercentSymbol); break; } } ns.AppendIntegerStringWithGroupSeparator (sb, nfi.PercentGroupSizes, nfi.PercentGroupSeparator); if (precision > 0) { sb.Append (nfi.PercentDecimalSeparator); ns.AppendDecimalString (precision, sb); } if (!needNegativeSign) { switch (nfi.PercentPositivePattern) { case 0: sb.Append (' '); sb.Append (nfi.PercentSymbol); break; case 1: sb.Append (nfi.PercentSymbol); break; } } else { switch (nfi.PercentNegativePattern) { case 0: sb.Append (' '); sb.Append (nfi.PercentSymbol); break; case 1: sb.Append (nfi.PercentSymbol); break; } } return sb.ToString (); } unsafe static string FormatHexadecimal (ulong value, bool positive, int byteSize, int precision, bool upper) { if (!positive) { /* for large values the cast to ulong is going to return 0 anyway (at least on x86, possibly a MS/Mono runtime bug) */ #if FALSE if (byteSize < 8) { value = (ulong)(Math.Pow (2, byteSize * 8)) - value; } else { value = 0 - value; } #else switch (byteSize) { case 1: value = (ulong)(256UL - value); break; case 2: value = (ulong)(65536UL - value); break; case 4: value = (ulong)(4294967296UL - value); break; case 8: value = 0 - value; break; } #endif } char[] digits = (upper ? digitUpperTable : digitLowerTable); int size = precision > 16 ? precision : 16; char* buffer = stackalloc char [size]; char* last = buffer + size; char* ptr = last; while (value > 0) { *--ptr = digits[value & 0xF]; value >>= 4; } while (ptr == last || last - ptr < precision) *--ptr = '0'; return new string (ptr, 0, (int)(last - ptr)); } internal static string FormatExponential (NumberStore ns, int precision, NumberFormatInfo nfi, bool upper) { if (precision < 0) precision = 6; if (ns.ZeroOnly) { StringBuilder sb = new StringBuilder (precision + nfi.PositiveSign.Length + 6); sb.Append ('0'); if (precision > 0) { sb.Append ('.'); sb.Append ('0', precision); } if (upper) sb.Append ('E'); else sb.Append ('e'); sb.Append (nfi.PositiveSign); sb.Append ('0', 3); return sb.ToString (); } int exponent = 0; while (!(ns.DecimalPointPosition == 1 && ns.GetChar (0) != '0')) { if (ns.DecimalPointPosition > 1) { ns.Divide10 (1); exponent ++; } else { ns.Multiply10 (1); exponent --; } } if (ns.RoundDecimal (precision)) { ns.Divide10 (1); exponent ++; } StringBuilder cb = new StringBuilder (ns.DecimalDigits + 1 + 8); if (!ns.Positive) { cb.Append (nfi.NegativeSign); } ns.AppendIntegerString (ns.IntegerDigits > 0 ? ns.IntegerDigits : 1, cb); if (precision > 0) { cb.Append (nfi.NumberDecimalSeparator); ns.AppendDecimalString (precision, cb); } if (upper) cb.Append ('E'); else cb.Append ('e'); if (exponent >= 0) cb.Append (nfi.PositiveSign); else { cb.Append (nfi.NegativeSign); exponent = -exponent; } if (exponent == 0) { cb.Append ('0', 3); } else if (exponent < 10) { cb.Append ('0', 2); cb.Append (digitLowerTable [exponent]); } else if (exponent < 100) { cb.Append ('0', 1); cb.Append (digitLowerTable [exponent / 10 % 10]); cb.Append (digitLowerTable [exponent % 10]); } else if (exponent < 1000) { cb.Append (digitLowerTable [exponent / 100 % 10]); cb.Append (digitLowerTable [exponent / 10 % 10]); cb.Append (digitLowerTable [exponent % 10]); /*} else { // exponent range is 0...+-324 int pos = cb.Length; int count = 3; while (exponent > 0 || --count > 0) { cb.Insert (pos, digitLowerTable [exponent % 10]); exponent /= 10; }*/ } return cb.ToString (); } #endregion #region Custom internal static string FormatCustom (string format, NumberStore ns, NumberFormatInfo nfi) { bool p = ns.Positive; int offset = 0; int length = 0; CustomInfo.GetActiveSection (format,ref p, ns.ZeroOnly, ref offset, ref length); if (length == 0) { return ns.Positive ? String.Empty : nfi.NegativeSign; } ns.Positive = p; CustomInfo info = CustomInfo.Parse (format, offset, length, 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(info.IntegerDigits * 2); StringBuilder sb_dec = new StringBuilder(info.DecimalDigits * 2); StringBuilder sb_exp = (info.UseExponent ? new StringBuilder(info.ExponentDigits * 2) : null); 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; } } expPositive = diff <= 0; NumberStore.AppendIntegerStringFromUInt32 (sb_exp, (uint)(diff >= 0 ? diff : -diff)); } else { ns.RoundDecimal (info.DecimalDigits); if (ns.ZeroOnly) ns.Positive = true; } if (info.IntegerDigits != 0 || !ns.CheckZeroOnlyInteger ()) { ns.AppendIntegerString (ns.IntegerDigits, sb_int); } /* 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); } */ ns.AppendDecimalString (ns.DecimalDigits, sb_dec); 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, offset, length, nfi, ns.Positive, sb_int, sb_dec, sb_exp); } 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 = 0; 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 void GetActiveSection (string format, ref bool positive, bool zero, ref int offset, ref int length) { 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) { offset = 0; length = format.Length; return; } if (index == 1) { if (positive || zero) { offset = 0; length = lens [0]; return; } if (lens [0] + 1 < format.Length) { positive = true; offset = lens [0] + 1; length = format.Length - offset; return; } else { offset = 0; length = lens [0]; return; } } if (index == 2) { if (zero) { offset = lens [0] + lens [1] + 2; length = format.Length - offset; return; } if (positive) { offset = 0; length = lens [0]; return; } if (lens [1] > 0) { positive = true; offset = lens [0] + 1; length = lens [1]; return; } else { offset = 0; length = lens [0]; return; } } if (index == 3) { if (zero) { offset = lens [0] + lens [1] + 2; length = lens [2]; return; } if (positive) { offset = 0; length = lens [0]; return; } if (lens [1] > 0) { positive = true; offset = lens [0] + 1; length = lens [1]; return; } else { offset = 0; length = lens [0]; return; } } throw new ArgumentException (); } public static CustomInfo Parse (string format, int offset, int length, 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 = offset; i - offset < 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 - offset < 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, int offset, int length, NumberFormatInfo nfi, bool positive, StringBuilder sb_int, StringBuilder sb_dec, StringBuilder 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 = nfi.NumberGroupSizes; string groupSeparator = nfi.NumberGroupSeparator; int intLen = 0, total = 0, groupIndex = 0, counter = 0, groupSize = 0, fraction = 0; if (UseGroup && groups.Length > 0) { intLen = sb_int.Length; for (int i = 0; i < groups.Length; i++) { total += groups [i]; if (total <= intLen) groupIndex = i; } groupSize = groups [groupIndex]; fraction = intLen > total ? intLen - total : 0; if (groupSize == 0) { while (groupIndex >= 0 && groups [groupIndex] == 0) groupIndex --; groupSize = fraction > 0 ? fraction : groups [groupIndex]; } if (fraction == 0) { counter = groupSize; } else { groupIndex += fraction / groupSize; counter = fraction % groupSize; if (counter == 0) counter = groupSize; else groupIndex ++; } } else { UseGroup = false; } for (int i = offset; i - offset < 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 - offset < 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++]); if (UseGroup && --intLen > 0 && --counter == 0) { sb.Append (groupSeparator); if (--groupIndex < groups.Length && groupIndex >= 0) groupSize = groups [groupIndex]; counter = groupSize; } } 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 - offset < 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 (); } } #endregion #region Internal structures internal struct NumberStore { bool _NaN; bool _infinity; bool _positive; int _decPointPos; int _defPrecision; int _defMaxPrecision; int _defByteSize; byte[] _digits; static uint [] IntList = new uint [] { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, }; static ulong [] ULongList = new ulong [] { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000, 100000000000, 1000000000000, 10000000000000, 100000000000000, 1000000000000000, 10000000000000000, 100000000000000000, 1000000000000000000, 10000000000000000000, }; #region Constructors public NumberStore (long value) { _infinity = _NaN = false; _defByteSize = 8; _defMaxPrecision = _defPrecision = 19; _positive = value >= 0; if (value == 0) { _digits = new byte []{0}; _decPointPos = 1; return; } ulong v = (ulong)(_positive ? value : -value); int i = 18, j = 0; if (v < 10) i = 0; else if (v < 100) i = 1; else if (v < 1000) i = 2; else if (v < 10000) i = 3; else if (v < 100000) i = 4; else if (v < 1000000) i = 5; else if (v < 10000000) i = 6; else if (v < 100000000) i = 7; else if (v < 1000000000) i = 8; else if (v < 10000000000) i = 9; else if (v < 100000000000) i = 10; else if (v < 1000000000000) i = 11; else if (v < 10000000000000) i = 12; else if (v < 100000000000000) i = 13; else if (v < 1000000000000000) i = 14; else if (v < 10000000000000000) i = 15; else if (v < 100000000000000000) i = 16; else if (v < 1000000000000000000) i = 17; else i = 18; _digits = new byte [i + 1]; do { ulong n = v / ULongList [i]; _digits [j++] = (byte)n; v -= ULongList [i--] * n; } while (i >= 0); _decPointPos = _digits.Length; } public NumberStore (int value) { _infinity = _NaN = false; _defByteSize = 4; _defMaxPrecision = _defPrecision = 10; _positive = value >= 0; if (value == 0) { _digits = new byte []{0}; _decPointPos = 1; return; } uint v = (uint)(_positive ? value : -value); int i = 9, j = 0; if (v < 10) i = 0; else if (v < 100) i = 1; else if (v < 1000) i = 2; else if (v < 10000) i = 3; else if (v < 100000) i = 4; else if (v < 1000000) i = 5; else if (v < 10000000) i = 6; else if (v < 100000000) i = 7; else if (v < 1000000000) i = 8; else i = 9; _digits = new byte [i + 1]; do { uint n = v / IntList [i]; _digits [j++] = (byte)n; v -= IntList [i--] * n; } while (i >= 0); _decPointPos = _digits.Length; } public NumberStore (short value) : this ((int)value) { _defByteSize = 2; _defMaxPrecision = _defPrecision = 5; } public NumberStore (sbyte value) : this ((int)value) { _defByteSize = 1; _defMaxPrecision = _defPrecision = 3; } public NumberStore (ulong value) { _infinity = _NaN = false; _defByteSize = 8; _defMaxPrecision = _defPrecision = 20; _positive = true; if (value == 0) { _digits = new byte []{0}; _decPointPos = 1; return; } int i = 19, j = 0; if (value < 10) i = 0; else if (value < 100) i = 1; else if (value < 1000) i = 2; else if (value < 10000) i = 3; else if (value < 100000) i = 4; else if (value < 1000000) i = 5; else if (value < 10000000) i = 6; else if (value < 100000000) i = 7; else if (value < 1000000000) i = 8; else if (value < 10000000000) i = 9; else if (value < 100000000000) i = 10; else if (value < 1000000000000) i = 11; else if (value < 10000000000000) i = 12; else if (value < 100000000000000) i = 13; else if (value < 1000000000000000) i = 14; else if (value < 10000000000000000) i = 15; else if (value < 100000000000000000) i = 16; else if (value < 1000000000000000000) i = 17; else if (value < 10000000000000000000) i = 18; else i = 19; _digits = new byte [i + 1]; do { ulong n = value / ULongList [i]; _digits [j++] = (byte)n; value -= ULongList [i--] * n; } while (i >= 0); _decPointPos = _digits.Length; } public NumberStore (uint value) { _infinity = _NaN = false; _positive = true; _defByteSize = 4; _defMaxPrecision = _defPrecision = 10; if (value == 0) { _digits = new byte []{0}; _decPointPos = 1; return; } int i = 9, j = 0; if (value < 10) i = 0; else if (value < 100) i = 1; else if (value < 1000) i = 2; else if (value < 10000) i = 3; else if (value < 100000) i = 4; else if (value < 1000000) i = 5; else if (value < 10000000) i = 6; else if (value < 100000000) i = 7; else if (value < 1000000000) i = 8; else i = 9; _digits = new byte [i + 1]; do { uint n = value / IntList [i]; _digits [j++] = (byte)n; value -= IntList [i--] * n; } while (i >= 0); _decPointPos = _digits.Length; } public NumberStore (ushort value) : this ((uint)value) { _defByteSize = 2; _defMaxPrecision = _defPrecision = 5; } public NumberStore (byte value) : this ((uint)value) { _defByteSize = 1; _defMaxPrecision = _defPrecision = 3; } public NumberStore(double value) { _digits = null; _defByteSize = 64; _defPrecision = 15; _defMaxPrecision = _defPrecision + 2; if (double.IsNaN (value) || double.IsInfinity (value)) { _NaN = double.IsNaN (value); _infinity = double.IsInfinity (value); _positive = value > 0; _decPointPos = 0; return; } else { _NaN = _infinity = false; } 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 [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); } public NumberStore(float value) { _digits = null; _defByteSize = 32; _defPrecision = 7; _defMaxPrecision = _defPrecision + 2; if (float.IsNaN (value) || float.IsInfinity (value)) { _NaN = float.IsNaN (value); _infinity = float.IsInfinity (value); _positive = value > 0; _decPointPos = 0; return; } else _infinity = _NaN = false; 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; } public NumberStore (decimal value) { int[] bits = decimal.GetBits (value); _positive = (bits [3] & 0x80000000) == 0; bits[3] = bits [3] & 0x7FFFFFFF; int ss = (bits [3] & 0x1F0000) >> 16; ulong lo = (ulong)((((ulong)bits[1]) << 32) | (uint)bits [0]); ulong hi = (uint)bits [2]; uint rest = 0; int digits = 0; while (hi > 0 || lo > 0) { digits ++; DivideDecimal (ref lo, ref hi, 10, ref rest); } lo = (ulong)((((ulong)bits[1]) << 32) | (uint)bits [0]); hi = (uint)bits [2]; _digits = new byte [digits]; int i = digits; while (hi > 0 || lo > 0) { DivideDecimal (ref lo, ref hi, 10, ref rest); _digits [--i] = (byte)rest; } _infinity = _NaN = false; _decPointPos = _digits.Length - ss; _defPrecision = _defMaxPrecision = 100; _defByteSize = 16; } static int DivideDecimal (ref ulong lo, ref ulong hi, uint factor, ref uint rest) { ulong a, b, c, h; h = hi; a = (uint)(h >> 32); b = a / factor; a -= b * factor; a <<= 32; a |= (uint) h; c = a / factor; a -= c * factor; a <<= 32; hi = b << 32 | (uint)c; h = lo; a |= (uint)(h >> 32); b = a / factor; a -= b * factor; a <<= 32; a |= (uint) h; c = a / factor; a -= c * factor; lo = b << 32 | (uint)c; rest = (uint)a; a <<= 1; return (a >= factor || (a == factor && (c & 1) == 1)) ? 1 : 0; } #endregion #region Public Property public bool IsNaN { get { return _NaN; } } public bool IsInfinity { get { return _infinity; } } public int DecimalPointPosition { get { return _decPointPos; } } public bool Positive { get { return _positive; } set { _positive = value;} } public int DefaultPrecision { get { return _defPrecision; } } public int DefaultMaxPrecision { get { return _defMaxPrecision; } } public int DefaultByteSize { get { return _defByteSize; } } public bool HasDecimal { get { return _digits.Length > _decPointPos; } } public int IntegerDigits { get { return _decPointPos > 0 ? _decPointPos : 1; } } public int DecimalDigits { get { return HasDecimal ? _digits.Length - _decPointPos : 0; } } public bool IsFloatingSource { get { return _defPrecision == 15 || _defPrecision == 7; } } public bool IsDecimalSource { get { return _defPrecision > 30; } } public bool IsBankerApplicable { get { if ((_digits == null) || (_digits.Length < 2)) return false; return ((_digits [_digits.Length - 2] & 1) == 1); } } public bool ZeroOnly { get { for (int i = 0; i < _digits.Length; i++) if (_digits [i] != 0) return false; return true; } } #endregion #region Public Method #region Round public bool RoundPos (int pos) { return RoundPos (pos, true); } public 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 bool RoundDecimal (int decimals) { return RoundDecimal (decimals, true, true); } public bool RoundDecimal (int decimals, bool carryFive, bool countZero) { bool carry = false; if (countZero || (_decPointPos > 0)) 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; } 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 bool RoundEffectiveDigits (int digits) { return RoundEffectiveDigits (digits, true, true); } public 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; /// are we cutting from the maximum precision ? if (_digits.Length == _defMaxPrecision) { // special case if we *aren't* cutting inside the extra precision (e.g. 16 on 17) if (digits != _defMaxPrecision - 1) { // ok, here we look at the *two* extra numbers we're keeping // (we keep 17 digits while the true precision is 15 digits). int extra = _digits[_defMaxPrecision - 2] * 10 + _digits[_defMaxPrecision - 1]; carry = (extra >= 50); if (carry) { _digits[_defMaxPrecision - 2] = 0; _digits[_defMaxPrecision - 1] = 0; int d = _digits.Length - 3; CarryPropagation (ref d, carryFive, ref carry); } } } CarryPropagation (ref digits, carryFive, ref carry); for (int i = digits; i < _digits.Length; i++) _digits [i] = 0; TrimDecimalEndZeros (); return carry; } private void CarryPropagation (ref int digits, bool carryFive, ref bool carry) { 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++; } } #endregion #region Trim public 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 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; } } #endregion #region Integer public void AppendIntegerString (int minLength, StringBuilder cb) { if (IntegerDigits == 0) { cb.Append ('0', minLength); return; } if (_decPointPos <= 0) { cb.Append ('0', minLength); return; } if (_decPointPos < minLength) cb.Append ('0', minLength - _decPointPos); for (int i = 0; i < _decPointPos; i++) { if (i < _digits.Length) cb.Append ((char)('0' + _digits [i])); else cb.Append ('0'); } } public void AppendIntegerStringWithGroupSeparator (StringBuilder sb, int[] groups, string groupSeparator) { if (_decPointPos <= 0) { sb.Append ('0'); return; } int intLen = IntegerDigits; int total = 0; int groupIndex = 0; for (int i = 0; i < groups.Length; i++) { total += groups [i]; if (total <= intLen) groupIndex = i; } if (groups.Length > 0 && total > 0) { int counter; int groupSize = groups [groupIndex]; int fraction = intLen > total ? intLen - total : 0; if (groupSize == 0) { while (groupIndex >= 0 && groups [groupIndex] == 0) groupIndex --; groupSize = fraction > 0 ? fraction : groups [groupIndex]; } if (fraction == 0) { counter = groupSize; } else { groupIndex += fraction / groupSize; counter = fraction % groupSize; if (counter == 0) counter = groupSize; else groupIndex ++; } for (int i = 0; i < _decPointPos; i++) { if (i < _digits.Length) { sb.Append ((char)('0' + _digits [i])); } else { sb.Append ('0'); } if (i < intLen - 1 && --counter == 0) { sb.Append (groupSeparator); if (--groupIndex < groups.Length && groupIndex >= 0) groupSize = groups [groupIndex]; counter = groupSize; } } } else { for (int i = 0; i < _decPointPos; i++) { if (i < _digits.Length) { sb.Append ((char)('0' + _digits [i])); } else { sb.Append ('0'); } } } } #endregion #region Decimal public 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 void AppendDecimalString (int precision, StringBuilder cb) { if (!HasDecimal) { cb.Append ('0', precision); return; } int i = _decPointPos; for (; i < _digits.Length && i < precision + _decPointPos; i++) { if (i >= 0) cb.Append ((char)('0' + _digits [i])); else cb.Append ('0'); } i -= _decPointPos; if (i < precision) cb.Append ('0', precision - i); } #endregion #region others public bool CheckZeroOnlyInteger () { for (int i = 0; i < _decPointPos && i < _digits.Length; i++) { if (_digits [i] != 0) return false; } return true; } public void Multiply10 (int count) { if (count <= 0) return; _decPointPos += count; TrimIntegerStartZeros (); } public void Divide10 (int count) { if (count <= 0) return; _decPointPos -= count; } public override string ToString() { StringBuilder sb = new StringBuilder (); AppendIntegerString (IntegerDigits, sb); if (HasDecimal) { sb.Append ('.'); AppendDecimalString (DecimalDigits, sb); } return sb.ToString (); } public 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 byte GetDigitByte (int pos) { if (_decPointPos <= 0) pos += _decPointPos - 1; if (pos < 0 || pos >= _digits.Length) return 0; else return _digits [pos]; } public 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; } public int GetDecimalPointPos () { return _decPointPos; } public void SetDecimalPointPos (int dp) { _decPointPos = dp; } #endregion #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; } public static void AppendIntegerStringFromUInt32 (StringBuilder sb, uint v) { if (v < 0) throw new ArgumentException (); int i = 9; if (v >= 1000000000) i = 9; else if (v >= 100000000) i = 8; else if (v >= 10000000) i = 7; else if (v >= 1000000) i = 6; else if (v >= 100000) i = 5; else if (v >= 10000) i = 4; else if (v >= 1000) i = 3; else if (v >= 100) i = 2; else if (v >= 10) i = 1; else i = 0; do { uint n = v / IntList [i]; sb.Append (NumberFormatter.digitLowerTable [n]); v -= IntList [i--] * n; } while (i >= 0); } #endregion } #endregion } }