#region NumberToString
public static string NumberToString (string format, sbyte value, NumberFormatInfo nfi)
{
- return NumberToString (format, new NumberStore (value), 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)
{
- return NumberToString (format, new NumberStore (value), 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)
{
- return NumberToString (format, new NumberStore (value), 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)
{
- return NumberToString (format, new NumberStore (value), 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)
{
- return NumberToString (format, new NumberStore (value), 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)
{
- return NumberToString (format, new NumberStore (value), 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)
{
- return NumberToString (format, new NumberStore (value), 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)
{
- return NumberToString (format, new NumberStore (value), 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)
{
- return NumberToString (format, new NumberStore (value), 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)
{
- return NumberToString (format, new NumberStore (value), 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)
{
- return NumberToString (format, new NumberStore (value), 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)
+ public static string NumberToString (string format, NumberStore ns, NumberFormatInfo nfi, char specifier, int precision, bool custom)
{
if (ns.IsNaN) {
return nfi.NaNSymbol;
return nfi.NegativeInfinitySymbol;
}
- char specifier;
- int precision;
- bool custom;
-
- if (format == null || format.Length == 0)
- format = "G";
-
- if (nfi == null)
+ if (nfi == null)
nfi = NumberFormatInfo.GetInstance (null);
- if (!ParseBasicFormat (format, out specifier, out precision, out custom))
- throw new FormatException (Locale.GetText ("The specified format '" + format + "' is invalid"));
-
if (custom){
if (ns.IsFloatingSource)
ns.RoundEffectiveDigits (ns.DefaultPrecision);
case 'g':
case 'G':
if (precision <= 0)
- ns.RoundEffectiveDigits (ns.DefaultPrecision);
+ ns.RoundEffectiveDigits (ns.DefaultPrecision, ns.IsBankerApplicable, true);
else
ns.RoundEffectiveDigits (precision);
break;
return FormatFixedPoint (ns, precision, nfi);
case 'g':
case 'G':
- return FormatGeneral (ns, precision, nfi, specifier == 'G', false);
+ 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);
} else {
throw new FormatException (Locale.GetText ("The specified format cannot be used in this instance"));
}
- case 'x':
- case 'X':
- return FormatHexadecimal (ns, precision, nfi, specifier == 'X');
default:
throw new FormatException (Locale.GetText ("The specified format '" + format + "' is invalid"));
}
#endregion
#region BasicParser
- private static bool ParseBasicFormat (string format, out char specifier, out int precision, out bool custom)
- {
+ 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 = '\0';
+ specifier = format[0];
custom = false;
-
- if (format.Length < 1)
- return false;
-
- specifier = format [0];
if (Char.IsLetter (specifier)) {
if (format.Length == 1)
- return true;
+ return;
bool flag = true;
precision = 0;
}
}
if (flag)
- return true;
+ return;
}
custom = true;
- return true;
+ return;
}
#endregion
if (ns.IsDecimalSource)
ns.RoundPos (precision);
else
- ns.RoundDecimal (precision);
+ ns.RoundDecimal (precision, true, false);
}
if (!ns.Positive) {
return sb.ToString ();
}
- internal static string FormatHexadecimal (NumberStore ns, int precision, NumberFormatInfo nfi, bool upper)
+ unsafe static string FormatHexadecimal (ulong value, bool positive, int byteSize, int precision, bool upper)
{
- if (ns.IsFloatingSource || ns.IsDecimalSource)
- throw new FormatException ();
-
- int intSize = ns.DefaultByteSize;
- ulong value = 0;
- for (int i = 0; i < ns.IntegerDigits; i++) {
- value *= 10;
- value += ns.GetDigitByte (i);
- }
-
- if (!ns.Positive) {
- value = (ulong)(Math.Pow (2, intSize * 8)) - value;
+ 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);
- CharBuffer sb = new CharBuffer (16 + precision + 1);
-
+ int size = precision > 16 ? precision : 16;
+ char* buffer = stackalloc char [size];
+ char* last = buffer + size;
+ char* ptr = last;
+
while (value > 0) {
- sb.InsertToFront (digits [value % 16]);
+ *--ptr = digits[value & 0xF];
value >>= 4;
}
- if (sb.Length == 0)
- sb.InsertToFront ('0');
+ while (ptr == last || last - ptr < precision)
+ *--ptr = '0';
- if (sb.Length < precision)
- sb.InsertToFront ('0', precision - sb.Length);
-
- return sb.ToString ();
+ return new string (ptr, 0, (int)(last - ptr));
}
internal static string FormatExponential (NumberStore ns, int precision, NumberFormatInfo nfi, bool upper)
{
int length = 0;
CustomInfo.GetActiveSection (format,ref p, ns.ZeroOnly, ref offset, ref length);
if (length == 0) {
- return ns.Positive ? "" : nfi.NegativeSign;
+ return ns.Positive ? String.Empty : nfi.NegativeSign;
}
ns.Positive = p;
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++)
}
public bool RoundDecimal (int decimals)
{
- return RoundDecimal (decimals, true);
+ return RoundDecimal (decimals, true, true);
}
- public bool RoundDecimal (int decimals, bool carryFive)
+ public bool RoundDecimal (int decimals, bool carryFive, bool countZero)
{
bool carry = false;
- decimals += _decPointPos;
+ if (countZero || (_decPointPos > 0))
+ decimals += _decPointPos;
if (!HasDecimal || decimals >= _digits.Length)
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)
}
if (carry) {
- byte[] temp = new byte [_digits.Length + 1];
+ byte[] temp = new byte[_digits.Length + 1];
_digits.CopyTo (temp, 1);
- temp [0] = 1;
+ temp[0] = 1;
_digits = temp;
- _decPointPos ++;
- digits ++;
+ _decPointPos++;
+ digits++;
}
-
- for (int i = digits; i < _digits.Length; i++)
- _digits [i] = 0;
- TrimDecimalEndZeros ();
-
- return carry;
}
+
#endregion
#region Trim
}
#endregion
}
- internal struct CharBuffer
- {
- int offset;
- char[] buffer;
-
- public CharBuffer (int capacity)
- {
- buffer = new char [capacity];
- offset = capacity;
- }
-
- void AllocateBuffer (int size)
- {
- size = size > buffer.Length * 2 ? size : buffer.Length * 2;
- char[] newBuffer = new char [size];
- offset += size - buffer.Length;
- Array.Copy (buffer, 0, newBuffer, size - buffer.Length, buffer.Length);
- buffer = newBuffer;
- }
-
- void CheckInsert (int length)
- {
- if (offset - length < 0) {
- AllocateBuffer (buffer.Length + length - offset);
- }
- }
-
- public void InsertToFront (char c)
- {
- CheckInsert (1);
- buffer [--offset] = c;
- }
-
- public void InsertToFront (char c, int repeat)
- {
- CheckInsert (repeat);
- while (repeat-- > 0) {
- buffer [--offset] = c;
- }
- }
-
- public char this [int index]
- {
- get {
- return buffer [offset + index];
- }
- }
-
- public override string ToString()
- {
- if (offset == buffer.Length)
- return "";
-
- return new string (buffer, offset, buffer.Length - offset);
- }
-
- public int Length {
- get { return buffer.Length - offset; }
- }
- }
#endregion
}
}