Permalink
Branch: master
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
2315 lines (2070 sloc) 56.6 KB
#if !LOCALTEST
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("The specified format cannot be used in this instance");
}
default:
throw new FormatException("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();
}
static string FormatHexadecimal(ulong value, bool positive, int byteSize, int precision, bool upper) {
if (!positive) {
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;
}
}
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));*/
char[] buffer = new char[size];
int idx = size;
while (value > 0) {
uint valTemp = (uint)value;
valTemp &= 0xf;
buffer[--idx] = digits[valTemp];
value >>= 4;
}
while (idx == size || size - idx < precision) {
buffer[--idx] = '0';
}
return new string(buffer, idx, size - idx);
}
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
}
}
#endif