csharp/EPAM.Deltix.DFP/DotNetImpl.cs (1,681 lines of code) (raw):
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using BID_UINT64 = System.UInt64;
using BID_UINT32 = System.UInt32;
using _IDEC_flags = System.UInt32;
[assembly: InternalsVisibleToAttribute("EPAM.Deltix.DFP.Test")]
namespace EPAM.Deltix.DFP
{
internal static class DotNetImpl
{
#region Constants
public const UInt64 PositiveInfinity = 0x7800000000000000UL;
public const UInt64 NegativeInfinity = 0xF800000000000000UL;
public const UInt64 NaN = 0x7C00000000000000UL;
public const UInt64 Null = 0xFFFFFFFFFFFFFF80UL; // = -0x80
public const UInt64 MinValue = 0xF7FB86F26FC0FFFFUL;
public const UInt64 MaxValue = 0x77FB86F26FC0FFFFUL;
public const UInt64 MinPositiveValue = 0x0000000000000001UL;
public const UInt64 MaxNegativeValue = 0x8000000000000001UL;
public const UInt64 Zero = 0x31C0000000000000UL; // e=0, m=0, sign=0
public const UInt64 One = Zero + 1; // = Zero + 1
public const UInt64 Two = Zero + 2;
public const UInt64 Ten = Zero + 10;
public const UInt64 Hundred = Zero + 100;
public const UInt64 Thousand = Zero + 1000;
public const UInt64 Million = Zero + 1000000;
public const UInt64 OneTenth = 0x31A0000000000000UL + 1;
public const UInt64 OneHundredth = 0x3180000000000000UL + 1;
public const UInt64 LONG_LOW_PART = 0xFFFFFFFFUL;
public static BID_UINT64[] PowersOfTen = {
/* 0 */ 1L,
/* 1 */ 10L,
/* 2 */ 100L,
/* 3 */ 1000L,
/* 4 */ 10000L,
/* 5 */ 100000L,
/* 6 */ 1000000L,
/* 7 */ 10000000L,
/* 8 */ 100000000L,
/* 9 */ 1000000000L,
/* 10 */ 10000000000L,
/* 11 */ 100000000000L,
/* 12 */ 1000000000000L,
/* 13 */ 10000000000000L,
/* 14 */ 100000000000000L,
/* 15 */ 1000000000000000L,
/* 16 */ 10000000000000000L,
/* 17 */ 100000000000000000L,
/* 18 */ 1000000000000000000L
};
/**
* Computes number of dfp digits required to represent the given non-negative value.
*
* @param value Non-negative value.
* @return Number of digits required.
*/
public static int NumberOfDigits(BID_UINT64 value)
{
for (int i = 1; i < PowersOfTen.Length; ++i)
if (value < PowersOfTen[i])
return i;
return 19;
}
#endregion
#region Classification
public static Boolean IsNaN(UInt64 value)
{
return (value & NaNMask) == NaNMask;
}
public static Boolean IsNull(UInt64 value)
{
return value == Null;
}
public static Boolean IsInfinity(UInt64 value)
{
return (value & (InfinityMask | NaNMask)) == InfinityMask;
}
public static Boolean IsPositiveInfinity(UInt64 value)
{
return (value & (SignedInfinityMask | NaNMask)) == PositiveInfinity;
}
public static Boolean IsNegativeInfinity(UInt64 value)
{
return (value & (SignedInfinityMask | NaNMask)) == NegativeInfinity;
}
[Obsolete("IsSigned is deprecated, please use IsNegative instead for actual comparison with 0")]
public static Boolean IsSigned(UInt64 value)
{
return SignBit(value);
}
public static Boolean IsFinite(UInt64 value)
{
return (value & InfinityMask) != InfinityMask;
}
internal static Boolean SignBit(UInt64 value)
{
return (Int64)value < 0;
}
#endregion
#region Conversion
public static UInt64 FromInt32(Int32 value)
{
Int64 longValue = value; // Fixes -Int32.MinValue
return value >= 0 ? (0x31C00000UL << 32) | (UInt64)longValue : (0xB1C00000UL << 32) | (UInt64)(-longValue);
}
public static UInt64 FromUInt32(UInt32 value)
{
return (0x31C00000UL << 32) | value;
}
// Unused
public static UInt64 FromInt16(Int16 value)
{
Int32 intValue = value;
return intValue >= 0 ? (0x31C00000UL << 32) | (UInt32)value : (0xB1C00000UL << 32) | (UInt32)(-value);
}
public static UInt64 FromFixedPointFastUnchecked(Int64 mantissa, int numDigits)
{
Int64 sign = mantissa >> 63;
return (UInt64)mantissa & SignMask | (UInt64)(mantissa + sign ^ sign) | ((UInt64)(BaseExponent - numDigits) << 53);
}
public static UInt64 FromFixedPointFastUnsignedUnchecked(UInt64 mantissaUlong, int numDigits)
{
return mantissaUlong + ((UInt64)(BaseExponent - numDigits) << 53);
}
public static UInt64 FromFixedPointFastUnsignedUnchecked(UInt32 mantissaUint, int numDigits)
{
return FromFixedPointFastUnsignedUnchecked((UInt64)mantissaUint, numDigits);
}
public static UInt64 FromFixedPointFast(Int32 mantissa, int numDigits)
{
UInt64 rv = FromFixedPointFastUnchecked(mantissa, numDigits);
if (numDigits + (Int32.MinValue + BiasedExponentMaxValue - BaseExponent) > (Int32.MinValue + BiasedExponentMaxValue))
throw new ArgumentException();
return rv;
}
public static UInt64 FromFixedPointFastUnsigned(UInt32 mantissaUint, int numDigits)
{
UInt64 rv = FromFixedPointFastUnsignedUnchecked(mantissaUint, numDigits);
if (numDigits + (Int32.MinValue + BiasedExponentMaxValue - BaseExponent) > (Int32.MinValue + BiasedExponentMaxValue))
throw new ArgumentException();
return rv;
}
public static UInt64 FromFixedPoint32(int mantissa, int numDigits)
{
// TODO: Unsigned comparison could be slightly faster, maybe
return numDigits + (Int32.MinValue + BiasedExponentMaxValue - BaseExponent) > (Int32.MinValue + BiasedExponentMaxValue)
? NativeImpl.fromFixedPoint64(mantissa, numDigits)
: FromFixedPointFastUnchecked(mantissa, numDigits);
}
public static UInt64 FromFixedPointLimitedU64(UInt64 mantissa, int numDigits)
{
Debug.Assert(mantissa < (1UL << 53));
return numDigits + (Int32.MinValue + BiasedExponentMaxValue - BaseExponent) > (Int32.MinValue + BiasedExponentMaxValue)
? NativeImpl.fromFixedPoint64((Int64)mantissa, numDigits)
: FromFixedPointFastUnsignedUnchecked(mantissa, numDigits);
}
public static UInt64 FromFixedPoint32(UInt32 mantissa, int numDigits)
{
// TODO: Unsigned comparison could be slightly faster, maybe
return numDigits + (Int32.MinValue + BiasedExponentMaxValue - BaseExponent) > (Int32.MinValue + BiasedExponentMaxValue)
? NativeImpl.fromFixedPoint64(mantissa, numDigits)
: FromFixedPointFastUnsignedUnchecked(mantissa, numDigits);
}
// The internals from the https://referencesource.microsoft.com/#mscorlib/system/decimal.cs
[StructLayout(LayoutKind.Sequential)]
private struct DecimalNet
{
// The lo, mid, hi, and flags fields contain the representation of the
// Decimal value. The lo, mid, and hi fields contain the 96-bit integer
// part of the Decimal. Bits 0-15 (the lower word) of the flags field are
// unused and must be zero; bits 16-23 contain must contain a value between
// 0 and 28, indicating the power of 10 to divide the 96-bit integer part
// by to produce the Decimal value; bits 24-30 are unused and must be zero;
// and finally bit 31 indicates the sign of the Decimal value, 0 meaning
// positive and 1 meaning negative.
//
// NOTE: Do not change the order in which these fields are declared. The
// native methods in this class rely on this particular order.
public uint flags;
public uint hi;
public uint lo;
public uint mid;
}
public static UInt64 FromDecimal(Decimal dec)
{
unsafe
{
DecimalNet* decPtr = (DecimalNet*)&dec;
ulong sign = ((ulong)decPtr->flags & 0x80000000UL) << 32;
int exp = -((int)(decPtr->flags >> 16) & 0xFF);
ulong mantissa;
if (decPtr->hi == 0)
{
mantissa = (((ulong)decPtr->mid) << 32) | ((ulong)decPtr->lo);
}
else
{
Pair96 pair96 = new Pair96(decPtr->lo, ((ulong)decPtr->mid) | (((ulong)decPtr->hi) << 32));
while (pair96.w21 > int.MaxValue)
{
pair96.Div(10);
exp++;
}
mantissa = (pair96.w21 << 32) | pair96.w0;
}
BID_UINT32 fpsf = DotNetReImpl.BID_EXACT_STATUS;
return DotNetReImpl.get_BID64(sign, exp + DotNetReImpl.DECIMAL_EXPONENT_BIAS, mantissa, DotNetReImpl.BID_ROUNDING_TO_NEAREST, ref fpsf);
}
}
public static Decimal ToDecimalFallback(UInt64 value)
{
return new Decimal(NativeImpl.toFloat64(value));
}
public static Decimal ToDecimal(UInt64 value)
{
bool signBit = (Int64)value < 0;
Int32 exponent;
UInt64 mantissa;
if ((~value & SpecialEncodingMask) == 0) //if ((x & SpecialEncodingMask) == SpecialEncodingMask)
{
Int32 exp2;
mantissa = UnpackSpecial(value, out exp2);
exponent = BaseExponent - exp2;
}
else
{
// Extract the exponent.
exponent = BaseExponent - (int)(value >> ExponentShiftSmall) & (int)ShiftedExponentMask;
// Extract the coefficient.
mantissa = value & SmallCoefficientMask;
}
if ((UInt32)exponent < 29)
return new Decimal((int)mantissa, (int)(mantissa >> 32), 0, signBit, (byte)exponent);
return ToDecimalFallback(value);
}
internal static UInt64 FromDecimalFloat64(Double x)
{
unchecked
{
UInt64 y = NativeImpl.fromFloat64(x);
// Using signed values because unsigned division optimizations are worse than signed
// in MS .NET, esp. Core2.0 while it should be opposite
Int64 m;
UInt64 signAndExp;
UInt64 notY = ~y;
if ((SpecialEncodingMask & notY) == 0)
{
// Special value or large coefficient
if ((InfinityMask & notY) == 0)
return y; // Infinity etc.
m = (Int64)((y & LargeCoefficientMask) + LargeCoefficientHighBits);
//signAndExp = ((y << 2) & SmallCoefficientExponentMask) + (y & SignMask);
signAndExp = (y & LargeCoefficientExponentMask) * 4 + (y & SignMask);
}
else
{
// "Normal" value
m = (Int64)(y & SmallCoefficientMask);
// 16 digits + odd
signAndExp = y & (UInt64.MaxValue << ExponentShiftSmall);
if ((UInt64)m < MaxCoefficient / 10 + 1)
return y;
}
if ((y & 1) == 0)
return y;
// Check the last digit
Int64 m1 = m + 1;
m = m1 / 10;
if (m1 - m * 10 > 2)
return y;
signAndExp += 1L << ExponentShiftSmall;
if (NativeImpl.toFloat64(signAndExp + (UInt64)m) != x)
return y;
for (Int64 n = m; ;)
{
Int64 mNext = n / 10;
if (mNext * 10 != n)
return signAndExp + (UInt64)n;
n = mNext;
signAndExp += 1L << ExponentShiftSmall;
}
}
}
#endregion
#region Comparison
public static Boolean IsZero(UInt64 value)
{
if (!IsFinite(value))
return false;
if ((value & SteeringBitsMask) == SteeringBitsMask)
return ((value & MaskBinarySig2) | MaskBinaryOr2) > 9999999999999999L;
return (value & MaskBinarySig1) == 0;
}
public static Boolean IsPositive(UInt64 value)
{
if ((value & NaNMask) == NaNMask)
return false;
if (IsZero(value))
return false;
return (value & SignMask) == 0;
}
public static Boolean IsNegative(UInt64 value)
{
if ((value & NaNMask) == NaNMask)
return false;
if (IsZero(value))
return false;
return (value & SignMask) != 0;
}
public static Boolean IsNonPositive(UInt64 value)
{
if ((value & NaNMask) == NaNMask)
return false;
if (IsZero(value))
return true;
return (value & SignMask) != 0;
}
public static Boolean IsNonNegative(UInt64 value)
{
if ((value & NaNMask) == NaNMask)
return false;
if (IsZero(value))
return true;
return (value & SignMask) == 0;
}
#endregion
#region Arithmetic
public static UInt64 Negate(UInt64 value)
{
return value ^ SignMask;
}
public static UInt64 Abs(UInt64 value)
{
return value & ~SignMask;
}
#endregion
#region Rounding
public static UInt64 Round(UInt64 value, int n, RoundingMode roundType)
{
if (!IsFinite(value))
return value;
if (n > MaxExponent)
return value;
if (n < MinExponent)
return Zero;
BID_UINT64 partsSignMask;
int partsExponent;
BID_UINT64 partsCoefficient;
// DotNetReImpl.unpack_BID64(out partsSignMask, out partsExponent, out partsCoefficient, value);
{ // Copy-paste the toParts method for speedup
partsSignMask = value & 0x8000000000000000UL;
if ((value & DotNetReImpl.SPECIAL_ENCODING_MASK64) == DotNetReImpl.SPECIAL_ENCODING_MASK64)
{
//if ((value & DotNetReImpl.INFINITY_MASK64) == DotNetReImpl.INFINITY_MASK64) - Non finite values are already checked
//{
// partsExponent = 0;
// partsCoefficient = value & 0xfe03ffffffffffffUL;
// if ((value & 0x0003ffffffffffffUL) >= 1000000000000000UL)
// partsCoefficient = value & 0xfe00000000000000UL;
// if ((value & DotNetReImpl.NAN_MASK64) == DotNetReImpl.INFINITY_MASK64)
// partsCoefficient = value & DotNetReImpl.SINFINITY_MASK64;
// return 0; // NaN or Infinity
//} else
{
// Check for non-canonical values.
BID_UINT64 coeff = (value & DotNetReImpl.LARGE_COEFF_MASK64) | DotNetReImpl.LARGE_COEFF_HIGH_BIT64;
// check for non-canonical values
if (coeff >= 10000000000000000UL)
coeff = 0;
partsCoefficient = coeff;
// get exponent
BID_UINT64 tmp = value >> DotNetReImpl.EXPONENT_SHIFT_LARGE64;
partsExponent = (int)(tmp & DotNetReImpl.EXPONENT_MASK64);
}
}
else
{
// exponent
BID_UINT64 tmp = value >> DotNetReImpl.EXPONENT_SHIFT_SMALL64;
partsExponent = (int)(tmp & DotNetReImpl.EXPONENT_MASK64);
// coefficient
partsCoefficient = (value & DotNetReImpl.SMALL_COEFF_MASK64);
}
}
if (partsCoefficient == 0)
return Zero;
int exponent = partsExponent - DotNetReImpl.DECIMAL_EXPONENT_BIAS + n;
if (exponent >= 0) // value is already rounded
return value;
// All next - negative exponent case
BID_UINT64 divFactor;
int addExponent = 0;
{ // Truncate all digits except last one
int absPower = -exponent;
if (absPower >= MaxFormatDigits)
{
divFactor = MaxCoefficient + 1;
int expShift = MaxFormatDigits;
addExponent = absPower - expShift;
}
else
{
divFactor = PowersOfTen[absPower];
}
}
// Process last digit
switch (roundType)
{
case RoundingMode.Up:
partsCoefficient = addExponent == 0 ? ((partsCoefficient + divFactor - 1) / divFactor) * divFactor : divFactor;
break;
case RoundingMode.Down:
partsCoefficient = addExponent == 0 ? (partsCoefficient / divFactor) * divFactor : 0;
break;
case RoundingMode.Ceiling:
if (partsSignMask == 0/*!parts.isNegative()*/)
partsCoefficient = addExponent == 0 ? ((partsCoefficient + divFactor - 1) / divFactor) * divFactor : divFactor;
else
partsCoefficient = addExponent == 0 ? (partsCoefficient / divFactor) * divFactor : 0;
break;
case RoundingMode.Floor:
if (partsSignMask == 0/*!parts.isNegative()*/)
partsCoefficient = addExponent == 0 ? (partsCoefficient / divFactor) * divFactor : 0;
else
partsCoefficient = addExponent == 0 ? ((partsCoefficient + divFactor - 1) / divFactor) * divFactor : divFactor;
break;
case RoundingMode.HalfUp:
partsCoefficient = addExponent == 0 ? ((partsCoefficient + divFactor / 2) / divFactor) * divFactor : 0;
break;
case RoundingMode.HalfDown:
partsCoefficient = addExponent == 0 ? ((partsCoefficient + divFactor / 2 - 1) / divFactor) * divFactor : 0;
break;
case RoundingMode.HalfEven:
partsCoefficient = addExponent == 0 ? ((partsCoefficient + divFactor / 2 - 1 + ((partsCoefficient / divFactor) & 1L)) / divFactor) * divFactor : 0;
break;
case RoundingMode.Unnecessary:
if (addExponent != 0 /*&& partsCoefficient != 0 - always true: checked earlier*/ || partsCoefficient % divFactor != 0)
throw new ArithmeticException("Rounding necessary");
return value;
default:
throw new ArgumentException("Unsupported roundType(=" + roundType + ") value.");
}
partsExponent += addExponent;
if (partsCoefficient == 0)
return Zero;
BID_UINT32 fpsf = DotNetReImpl.BID_EXACT_STATUS;
return DotNetReImpl.get_BID64(partsSignMask, partsExponent, partsCoefficient, DotNetReImpl.BID_ROUNDING_TO_NEAREST, ref fpsf);
}
public static bool IsRounded(UInt64 value, int n)
{
if (!IsFinite(value))
return false;
if (n > MaxExponent)
return true;
//if (n < MinExponent)
// return Zero;
//BID_UINT64 partsSignMask; // No need sign check
int partsExponent;
BID_UINT64 partsCoefficient;
// DotNetReImpl.unpack_BID64(out partsSignMask, out partsExponent, out partsCoefficient, value);
{ // Copy-paste the toParts method for speedup
// partsSignMask = value & 0x8000000000000000UL;
if ((value & DotNetReImpl.SPECIAL_ENCODING_MASK64) == DotNetReImpl.SPECIAL_ENCODING_MASK64)
{
//if ((value & DotNetReImpl.INFINITY_MASK64) == DotNetReImpl.INFINITY_MASK64) - Non finite values are already checked
//{
// partsExponent = 0;
// partsCoefficient = value & 0xfe03ffffffffffffUL;
// if ((value & 0x0003ffffffffffffUL) >= 1000000000000000UL)
// partsCoefficient = value & 0xfe00000000000000UL;
// if ((value & DotNetReImpl.NAN_MASK64) == DotNetReImpl.INFINITY_MASK64)
// partsCoefficient = value & DotNetReImpl.SINFINITY_MASK64;
// return 0; // NaN or Infinity
//} else
{
// Check for non-canonical values.
BID_UINT64 coeff = (value & DotNetReImpl.LARGE_COEFF_MASK64) | DotNetReImpl.LARGE_COEFF_HIGH_BIT64;
// check for non-canonical values
if (coeff >= 10000000000000000UL) // So, partsCoefficient=0, so Zero is rounded
return true;
partsCoefficient = coeff;
// get exponent
BID_UINT64 tmp = value >> DotNetReImpl.EXPONENT_SHIFT_LARGE64;
partsExponent = (int)(tmp & DotNetReImpl.EXPONENT_MASK64);
}
}
else
{
// exponent
BID_UINT64 tmp = value >> DotNetReImpl.EXPONENT_SHIFT_SMALL64;
partsExponent = (int)(tmp & DotNetReImpl.EXPONENT_MASK64);
// coefficient
partsCoefficient = (value & DotNetReImpl.SMALL_COEFF_MASK64);
}
}
if (partsCoefficient == 0)
return true;
int exponent = partsExponent - DotNetReImpl.DECIMAL_EXPONENT_BIAS + n;
if (exponent >= 0) // value is already rounded
return true;
// All next - negative exponent case
{ // Truncate all digits except last one
int absPower = -exponent;
if (absPower > MaxFormatDigits)
{
return false;
}
else
{
BID_UINT64 divFactor = PowersOfTen[absPower];
return partsCoefficient % divFactor == 0;
}
}
}
public struct Pair96
{
public ulong w0;
public ulong w21;
public Pair96(ulong w0 = 0, ulong w21 = 0)
{
this.w0 = w0;
this.w21 = w21;
}
public void Mul(uint d)
{
ulong lowMul = w0 * d;
w0 = lowMul & LONG_LOW_PART;
w21 = w21 * d + (lowMul >> 32);
}
public void Mul(Factors96 f)
{
Mul(f.d01);
if (f.d02 > 1)
Mul(f.d02);
if (f.d03 > 1)
Mul(f.d03);
}
public void Div(uint d)
{
ulong r21 = w21 % d;
w21 /= d;
w0 = ((r21 << 32) | w0) / d;
}
public void Div(Factors96 f)
{
Div(f.d01);
if (f.d02 > 1)
Div(f.d02);
if (f.d03 > 1)
Div(f.d03);
}
public void Shr1()
{
w0 = ((w21 & 1) << 31) | (w0 >> 1);
w21 = w21 >> 1;
}
public void Set(Factors96 f)
{
ulong d12 = (ulong)f.d01 * f.d02;
w0 = LONG_LOW_PART & d12;
w21 = d12 >> 32;
if (f.d03 > 1)
Mul(f.d03);
}
public void Set(ulong w0, ulong w21)
{
this.w0 = w0;
this.w21 = w21;
}
public void Add(Pair96 p)
{
ulong lowPart = w0 + p.w0;
w0 = lowPart & LONG_LOW_PART;
w21 += p.w21 + (lowPart >> 32);
}
}
public struct Factors96
{
public uint d01, d02, d03;
}
public static UInt64 RoundToReciprocal(UInt64 value, uint r, RoundingMode roundType)
{
if (r < 1)
throw new ArgumentException("The r(=" + r + ") argument must be positive.");
if (!IsFinite(value))
return value;
//if (Math.log10(r) > JavaImpl.MAX_EXPONENT) // Never can happens
// return value;
//if (Math.log10(r) < JavaImpl.MIN_EXPONENT)
// return JavaImpl.ZERO;
BID_UINT64 partsSignMask;
int partsExponent;
BID_UINT64 partsCoefficient;
// DotNetReImpl.unpack_BID64(out partsSignMask, out partsExponent, out partsCoefficient, value);
{ // Copy-paste the toParts method for speedup
partsSignMask = value & 0x8000000000000000UL;
if ((value & DotNetReImpl.SPECIAL_ENCODING_MASK64) == DotNetReImpl.SPECIAL_ENCODING_MASK64)
{
//if ((value & DotNetReImpl.INFINITY_MASK64) == DotNetReImpl.INFINITY_MASK64) - Non finite values are already checked
//{
// partsExponent = 0;
// partsCoefficient = value & 0xfe03ffffffffffffUL;
// if ((value & 0x0003ffffffffffffUL) >= 1000000000000000UL)
// partsCoefficient = value & 0xfe00000000000000UL;
// if ((value & DotNetReImpl.NAN_MASK64) == DotNetReImpl.INFINITY_MASK64)
// partsCoefficient = value & DotNetReImpl.SINFINITY_MASK64;
// return 0; // NaN or Infinity
//} else
{
// Check for non-canonical values.
BID_UINT64 coeff = (value & DotNetReImpl.LARGE_COEFF_MASK64) | DotNetReImpl.LARGE_COEFF_HIGH_BIT64;
// check for non-canonical values
if (coeff >= 10000000000000000UL)
coeff = 0;
partsCoefficient = coeff;
// get exponent
BID_UINT64 tmp = value >> DotNetReImpl.EXPONENT_SHIFT_LARGE64;
partsExponent = (int)(tmp & DotNetReImpl.EXPONENT_MASK64);
}
}
else
{
// exponent
BID_UINT64 tmp = value >> DotNetReImpl.EXPONENT_SHIFT_SMALL64;
partsExponent = (int)(tmp & DotNetReImpl.EXPONENT_MASK64);
// coefficient
partsCoefficient = (value & DotNetReImpl.SMALL_COEFF_MASK64);
}
}
if (partsCoefficient == 0)
return Zero;
int unbiasedExponent = partsExponent - DotNetReImpl.DECIMAL_EXPONENT_BIAS;
if (unbiasedExponent >= 0) // value is already rounded
return value;
// Denormalize partsCoefficient to the maximal value to get the maximal precision after final r division
{
int dn = NumberOfDigits(partsCoefficient);
/*if (dn < PowersOfTen.Length - 1)*/
{
int expShift = (PowersOfTen.Length - 1) - dn;
partsExponent -= expShift;
unbiasedExponent -= expShift;
partsCoefficient *= PowersOfTen[expShift];
}
}
//Multiply partsCoefficient with r
Pair96 coefficientMulR = new Pair96();
{
ulong l0 = (LONG_LOW_PART & partsCoefficient) * r;
coefficientMulR.Set(LONG_LOW_PART & l0, (partsCoefficient >> 32) * r + (l0 >> 32));
}
//final long divFactor;
Factors96 divFactor; // divFactor = divFactor1 * divFactor2 * divFactor3
int addExponent;
{
int absPower = -unbiasedExponent;
int maxPower = Math.Min(absPower, Math.Min(3 * 9, NumberOfDigits(coefficientMulR.w21) + 10 /* low part */));
//divFactor = PowersOfTen[maxPower];
int factor1Power = Math.Min(maxPower, 9); // Int can hold max 1_000_000_000
divFactor.d01 = (uint)PowersOfTen[factor1Power];
int factor2Power = Math.Min(maxPower - factor1Power, 9);
divFactor.d02 = (uint)PowersOfTen[factor2Power];
divFactor.d03 = (uint)PowersOfTen[maxPower - factor1Power - factor2Power];
addExponent = absPower - maxPower;
}
// Process last digit
switch (roundType)
{
case RoundingMode.Up:
// partsCoefficient = addExponent == 0 ? ((partsCoefficient + divFactor - 1) / divFactor) * divFactor : divFactor;
if (addExponent != 0)
{
coefficientMulR.Set(divFactor);
}
else
{ // addExponent != 0
{ // + divFactor - 1
Pair96 divFactor96 = new Pair96();
divFactor96.Set(divFactor);
divFactor96.Add(new Pair96(0xFFFFFFFFUL, 0xFFFFFFFFFFFFFFFFUL));
coefficientMulR.Add(divFactor96);
}
coefficientMulR.Div(divFactor);
coefficientMulR.Mul(divFactor);
}
// partsCoefficient = addExponent == 0 ? ((partsCoefficient + divFactor - 1) / divFactor) * divFactor : divFactor;
break;
case RoundingMode.Down:
// partsCoefficient = addExponent == 0 ? (partsCoefficient / divFactor) * divFactor : 0;
if (addExponent != 0)
{
coefficientMulR.Set(0, 0);
}
else
{ // addExponent != 0
coefficientMulR.Div(divFactor);
coefficientMulR.Mul(divFactor);
}
// partsCoefficient = addExponent == 0 ? (partsCoefficient / divFactor) * divFactor : 0;
break;
case RoundingMode.Ceiling:
if (partsSignMask >= 0/*!parts.isNegative()*/)
{
// partsCoefficient = addExponent == 0 ? ((partsCoefficient + divFactor - 1) / divFactor) * divFactor : divFactor;
if (addExponent != 0)
{
coefficientMulR.Set(divFactor);
}
else
{ // addExponent != 0
{ // + divFactor - 1
Pair96 divFactor96 = new Pair96();
divFactor96.Set(divFactor);
divFactor96.Add(new Pair96(0xFFFFFFFFUL, 0xFFFFFFFFFFFFFFFFUL));
coefficientMulR.Add(divFactor96);
}
coefficientMulR.Div(divFactor);
coefficientMulR.Mul(divFactor);
}
// partsCoefficient = addExponent == 0 ? ((partsCoefficient + divFactor - 1) / divFactor) * divFactor : divFactor;
}
else
{ // partsSignMask >= 0
// partsCoefficient = addExponent == 0 ? (partsCoefficient / divFactor) * divFactor : 0;
if (addExponent != 0)
{
coefficientMulR.Set(0, 0);
}
else
{ // addExponent != 0
coefficientMulR.Div(divFactor);
coefficientMulR.Mul(divFactor);
}
// partsCoefficient = addExponent == 0 ? (partsCoefficient / divFactor) * divFactor : 0;
}
break;
case RoundingMode.Floor:
if (partsSignMask >= 0/*!parts.isNegative()*/)
{
// partsCoefficient = addExponent == 0 ? (partsCoefficient / divFactor) * divFactor : 0;
if (addExponent != 0)
{
coefficientMulR.Set(0, 0);
}
else
{ // addExponent != 0
coefficientMulR.Div(divFactor);
coefficientMulR.Mul(divFactor);
}
// partsCoefficient = addExponent == 0 ? (partsCoefficient / divFactor) * divFactor : 0;
}
else
{ // partsSignMask >= 0
// partsCoefficient = addExponent == 0 ? ((partsCoefficient + divFactor - 1) / divFactor) * divFactor : divFactor;
if (addExponent != 0)
{
coefficientMulR.Set(divFactor);
}
else
{ // addExponent != 0
{ // + divFactor - 1
Pair96 divFactor96 = new Pair96();
divFactor96.Set(divFactor);
divFactor96.Add(new Pair96(0xFFFFFFFFUL, 0xFFFFFFFFFFFFFFFFUL));
coefficientMulR.Add(divFactor96);
}
coefficientMulR.Div(divFactor);
coefficientMulR.Mul(divFactor);
}
// partsCoefficient = addExponent == 0 ? ((partsCoefficient + divFactor - 1) / divFactor) * divFactor : divFactor;
}
break;
case RoundingMode.HalfUp:
// partsCoefficient = addExponent == 0 ? ((partsCoefficient + divFactor / 2) / divFactor) * divFactor : 0;
if (addExponent != 0)
{
coefficientMulR.Set(0, 0);
}
else
{ // addExponent != 0
{ // + divFactor / 2
Pair96 divFactor96 = new Pair96();
divFactor96.Set(divFactor);
divFactor96.Shr1();
coefficientMulR.Add(divFactor96);
}
coefficientMulR.Div(divFactor);
coefficientMulR.Mul(divFactor);
}
// partsCoefficient = addExponent == 0 ? ((partsCoefficient + divFactor / 2) / divFactor) * divFactor : 0;
break;
case RoundingMode.HalfDown:
// partsCoefficient = addExponent == 0 ? ((partsCoefficient + divFactor / 2 - 1) / divFactor) * divFactor : 0;
if (addExponent != 0)
{
coefficientMulR.Set(0, 0);
}
else
{ // addExponent != 0
{ // + divFactor / 2
Pair96 divFactor96 = new Pair96();
divFactor96.Set(divFactor);
divFactor96.Shr1();
divFactor96.Add(new Pair96(0xFFFFFFFFUL, 0xFFFFFFFFFFFFFFFFUL));
coefficientMulR.Add(divFactor96);
}
coefficientMulR.Div(divFactor);
coefficientMulR.Mul(divFactor);
}
// partsCoefficient = addExponent == 0 ? ((partsCoefficient + divFactor / 2 - 1) / divFactor) * divFactor : 0;
break;
case RoundingMode.HalfEven:
// partsCoefficient = addExponent == 0 ? ((partsCoefficient + divFactor / 2 - 1 + ((partsCoefficient / divFactor) & 1L)) / divFactor) * divFactor : 0;
if (addExponent != 0)
{
coefficientMulR.Set(0, 0);
}
else
{ // addExponent != 0
{ // + divFactor / 2
Pair96 divFactor96 = new Pair96();
divFactor96.Set(divFactor);
divFactor96.Shr1();
bool divisionLatestBit;
{ // ((partsCoefficient / divFactor) & 1L)
Pair96 tmpCoefficientMulR = new Pair96(coefficientMulR.w0, coefficientMulR.w21);
tmpCoefficientMulR.Div(divFactor);
divisionLatestBit = (tmpCoefficientMulR.w0 & 1) != 0;
}
if (!divisionLatestBit)
divFactor96.Add(new Pair96(0xFFFFFFFFUL, 0xFFFFFFFFFFFFFFFFUL));
coefficientMulR.Add(divFactor96);
}
coefficientMulR.Div(divFactor);
coefficientMulR.Mul(divFactor);
}
// partsCoefficient = addExponent == 0 ? ((partsCoefficient + divFactor / 2 - 1 + ((partsCoefficient / divFactor) & 1L)) / divFactor) * divFactor : 0;
break;
case RoundingMode.Unnecessary:
if (!IsRoundedToReciprocalImpl(addExponent, coefficientMulR, divFactor))
throw new ArithmeticException("Rounding necessary");
return value;
default:
throw new ArgumentException("Unsupported roundType(=" + roundType + ") value.");
}
{ // / r
coefficientMulR.Div(r);
if (coefficientMulR.w21 > Int32.MaxValue)
{
int dn = NumberOfDigits(coefficientMulR.w21 / Int32.MaxValue);
uint f = (uint)PowersOfTen[dn];
partsExponent += dn;
coefficientMulR.Div(f);
}
partsCoefficient = ((LONG_LOW_PART & coefficientMulR.w21) << 32) + coefficientMulR.w0;
}
partsExponent += addExponent;
if (partsCoefficient == 0)
return Zero;
BID_UINT32 fpsf = DotNetReImpl.BID_EXACT_STATUS;
return DotNetReImpl.get_BID64(partsSignMask, partsExponent, partsCoefficient, DotNetReImpl.BID_ROUNDING_TO_NEAREST, ref fpsf);
}
public static bool IsRoundedToReciprocal(UInt64 value, uint r)
{
if (r < 1)
throw new ArgumentException("The r(=" + r + ") argument must be positive.");
if (!IsFinite(value))
return false;
//if (Math.log10(r) > JavaImpl.MAX_EXPONENT) // Never can happens
// return value;
//if (Math.log10(r) < JavaImpl.MIN_EXPONENT)
// return JavaImpl.ZERO;
BID_UINT64 partsSignMask;
int partsExponent;
BID_UINT64 partsCoefficient;
// DotNetReImpl.unpack_BID64(out partsSignMask, out partsExponent, out partsCoefficient, value);
{ // Copy-paste the toParts method for speedup
partsSignMask = value & 0x8000000000000000UL;
if ((value & DotNetReImpl.SPECIAL_ENCODING_MASK64) == DotNetReImpl.SPECIAL_ENCODING_MASK64)
{
//if ((value & DotNetReImpl.INFINITY_MASK64) == DotNetReImpl.INFINITY_MASK64) - Non finite values are already checked
//{
// partsExponent = 0;
// partsCoefficient = value & 0xfe03ffffffffffffUL;
// if ((value & 0x0003ffffffffffffUL) >= 1000000000000000UL)
// partsCoefficient = value & 0xfe00000000000000UL;
// if ((value & DotNetReImpl.NAN_MASK64) == DotNetReImpl.INFINITY_MASK64)
// partsCoefficient = value & DotNetReImpl.SINFINITY_MASK64;
// return 0; // NaN or Infinity
//} else
{
// Check for non-canonical values.
BID_UINT64 coeff = (value & DotNetReImpl.LARGE_COEFF_MASK64) | DotNetReImpl.LARGE_COEFF_HIGH_BIT64;
// check for non-canonical values
if (coeff >= 10000000000000000UL)
coeff = 0;
partsCoefficient = coeff;
// get exponent
BID_UINT64 tmp = value >> DotNetReImpl.EXPONENT_SHIFT_LARGE64;
partsExponent = (int)(tmp & DotNetReImpl.EXPONENT_MASK64);
}
}
else
{
// exponent
BID_UINT64 tmp = value >> DotNetReImpl.EXPONENT_SHIFT_SMALL64;
partsExponent = (int)(tmp & DotNetReImpl.EXPONENT_MASK64);
// coefficient
partsCoefficient = (value & DotNetReImpl.SMALL_COEFF_MASK64);
}
}
if (partsCoefficient == 0)
return true;
int unbiasedExponent = partsExponent - DotNetReImpl.DECIMAL_EXPONENT_BIAS;
if (unbiasedExponent >= 0) // value is already rounded
return true;
// Denormalize partsCoefficient to the maximal value to get the maximal precision after final r division
{
int dn = NumberOfDigits(partsCoefficient);
/*if (dn < PowersOfTen.Length - 1)*/
{
int expShift = (PowersOfTen.Length - 1) - dn;
partsExponent -= expShift;
unbiasedExponent -= expShift;
partsCoefficient *= PowersOfTen[expShift];
}
}
//Multiply partsCoefficient with r
Pair96 coefficientMulR = new Pair96();
{
ulong l0 = (LONG_LOW_PART & partsCoefficient) * r;
coefficientMulR.Set(LONG_LOW_PART & l0, (partsCoefficient >> 32) * r + (l0 >> 32));
}
//final long divFactor;
Factors96 divFactor; // divFactor = divFactor1 * divFactor2 * divFactor3
int addExponent;
{
int absPower = -unbiasedExponent;
int maxPower = Math.Min(absPower, Math.Min(3 * 9, NumberOfDigits(coefficientMulR.w21) + 10 /* low part */));
//divFactor = PowersOfTen[maxPower];
int factor1Power = Math.Min(maxPower, 9); // Int can hold max 1_000_000_000
divFactor.d01 = (uint)PowersOfTen[factor1Power];
int factor2Power = Math.Min(maxPower - factor1Power, 9);
divFactor.d02 = (uint)PowersOfTen[factor2Power];
divFactor.d03 = (uint)PowersOfTen[maxPower - factor1Power - factor2Power];
addExponent = absPower - maxPower;
}
return IsRoundedToReciprocalImpl(addExponent, coefficientMulR, divFactor);
}
static bool IsRoundedToReciprocalImpl(int addExponent, Pair96 coefficientMulR, Factors96 divFactor)
{
//case RoundingMode.Unnecessary:
if (addExponent != 0 /*&& partsCoefficient != 0 - always true: checked earlier*/)
return false;
{ // if (partsCoefficient % divFactor != 0) throw new ArithmeticException("Rounding necessary");
{
ulong r21 = coefficientMulR.w21 % divFactor.d01;
ulong l = ((r21 << 32) | coefficientMulR.w0);
if (l % divFactor.d01 != 0)
return false;
coefficientMulR.w0 = l / divFactor.d01;
coefficientMulR.w21 /= divFactor.d01;
}
if (divFactor.d02 > 1)
{
ulong r21 = coefficientMulR.w21 % divFactor.d02;
ulong l = ((r21 << 32) | coefficientMulR.w0);
if (l % divFactor.d02 != 0)
return false;
coefficientMulR.w0 = l / divFactor.d02;
coefficientMulR.w21 /= divFactor.d02;
}
if (divFactor.d03 > 1)
{
ulong r21 = coefficientMulR.w21 % divFactor.d03;
ulong l = ((r21 << 32) | coefficientMulR.w0);
if (l % divFactor.d03 != 0)
return false;
// coefficientMulR.w0 = l / divFactor.d03; // No need division result
// coefficientMulR.w21 /= divFactor.d03; // No need division result
}
}
return true;
}
#endregion
#region Special
#endregion
#region Formatting & Parsing
private const int BcdTableDigits = 3;
private const int BcdDivider = 1000000000;
private const int BcdDividerGroups = 3; // log10(BCD_DIVIDER) / BCD_TABLE_DIGITS must be natural value
private static char[] BCD_TABLE = MakeBcdTable(BcdTableDigits);
private static char[] MakeBcdTable(int tenPowerMaxIndex)
{
int n = 1;
for (int i = 0; i < tenPowerMaxIndex; ++i)
n *= 10;
char[] table = new char[n * tenPowerMaxIndex];
char[] value = new char[tenPowerMaxIndex];
for (int i = 0; i < value.Length; ++i) // Array.Fill is not available in .NET Standard 2.0, but 2.1
value[i] = '0';
for (int i = 0, ib = 0; i < n; ++i)
{
for (int j = 0; j < tenPowerMaxIndex; ++j)
table[ib++] = value[j];
value[0] = (char)(value[0] + 1);
for (int j = 0; j < tenPowerMaxIndex - 1; ++j)
{
if (value[j] <= '9')
break;
else
{
value[j] = (char)(value[j] - 10);
value[j + 1] = (char)(value[j + 1] + 1);
}
}
}
return table;
}
private static unsafe int FormatUIntFromBcdTable(int value, char* buffer, int bi)
{
for (int blockIndex = 0; blockIndex < BcdDividerGroups; ++blockIndex)
{
int newValue = (int)((ulong)(2199023256L * value) >> 41);
int remainder = value - newValue * 1000;
//final int remainder = value - ((newValue << 10) - (newValue << 4) - (newValue << 3));
value = newValue;
for (int j = 0, ti = remainder * BcdTableDigits /* (remainder << 1) + remainder */; j < BcdTableDigits; ++j, ++ti)
buffer[--bi] = BCD_TABLE[ti];
}
return bi;
}
public static String ToString(UInt64 value, char decimalMark, bool floatStyle)
{
if (!IsFinite(value))
{
// Value is either Inf or NaN
// TODO: Do we need SNaN?
return IsNaN(value) ? "NaN" : SignBit(value) ? "-Infinity" : "Infinity";
}
Int32 partsExponent;
BID_UINT64 partsCoefficient;
if ((~value & SpecialEncodingMask) == 0) //if ((x & SpecialEncodingMask) == SpecialEncodingMask)
{
partsCoefficient = UnpackSpecial(value, out partsExponent);
}
else
{
// Extract the exponent.
partsExponent = (int)(value >> ExponentShiftSmall) & (int)ShiftedExponentMask;
// Extract the coefficient.
partsCoefficient = value & SmallCoefficientMask;
}
if (partsCoefficient == 0)
{
return !floatStyle ? "0" : ("0" + decimalMark + "0");
}
int exponent = partsExponent - ExponentBias;
unsafe
{
var bufferLength = 512;
var buffer = stackalloc char[bufferLength];
if (exponent >= 0)
{
int bi = bufferLength;
if (floatStyle)
{
buffer[--bi] = '0';
buffer[--bi] = decimalMark;
}
for (int i = 0; i < exponent; ++i)
buffer[--bi] = '0';
while (partsCoefficient > 0)
{
bi = FormatUIntFromBcdTable((int)(partsCoefficient % BcdDivider), buffer, bi);
partsCoefficient /= BcdDivider;
}
while (buffer[bi] == '0')
++bi;
if (SignBit(value))
buffer[--bi] = '-';
return new string(buffer, bi, bufferLength - bi);
}
else
{ // exponent < 0
int bi = bufferLength;
int digits = NumberOfDigits(partsCoefficient);
if (digits + exponent > 0)
{
ulong integralPart = partsCoefficient / PowersOfTen[-exponent];
ulong fractionalPart = partsCoefficient % PowersOfTen[-exponent];
while (fractionalPart > 0)
{
bi = FormatUIntFromBcdTable((int)(fractionalPart % BcdDivider), buffer, bi);
fractionalPart /= BcdDivider;
}
int written = bufferLength - bi /* already written */;
//if (written < -exponent /* must be written */)
for (int ei = 0, ee = -exponent - written; ei < ee; ++ei)
buffer[--bi] = '0';
bi = bufferLength + exponent; /* buffer.length - (-exponent) */
buffer[--bi] = decimalMark;
while (integralPart > 0)
{
bi = FormatUIntFromBcdTable((int)(integralPart % BcdDivider), buffer, bi);
integralPart /= BcdDivider;
}
while (buffer[bi] == '0')
++bi;
if (SignBit(value))
buffer[--bi] = '-';
int be = bufferLength;
while (buffer[be - 1] == '0')
--be;
if (buffer[be - 1] == decimalMark)
{
if (!floatStyle)
{
--be;
}
else if (be < bufferLength)
{
buffer[be++] = '0';
}
}
return new string(buffer, bi, be - bi);
}
else
{
while (partsCoefficient > 0)
{
bi = FormatUIntFromBcdTable((int)(partsCoefficient % BcdDivider), buffer, bi);
partsCoefficient /= BcdDivider;
}
int written = bufferLength - bi /* already written */;
//if (written < -exponent /* must be written */)
for (int ei = 0, ee = -exponent - written; ei < ee; ++ei)
buffer[--bi] = '0';
bi = bufferLength + exponent; /* buffer.length - (-exponent) */
buffer[--bi] = decimalMark;
buffer[--bi] = '0';
if (SignBit(value))
buffer[--bi] = '-';
int be = bufferLength;
while (buffer[be - 1] == '0')
--be;
return new string(buffer, bi, be - bi);
}
}
}
}
public static string ToScientificString(UInt64 value, char decimalMark)
{
if (IsNull(value))
return "null";
if (!IsFinite(value))
{
// Value is either Inf or NaN
// TODO: Do we need SNaN?
return IsNaN(value) ? "NaN" : SignBit(value) ? "-Infinity" : "Infinity";
}
bool sign;
int exponent;
UInt64 coefficient = Unpack(value, out sign, out exponent);
if (coefficient == 0)
return "0" + decimalMark + "000000000000000e+000";
exponent -= BaseExponent;
unsafe
{
char* buffer = stackalloc char[MaxFormatDigits * 4];
char* bi = buffer + MaxFormatDigits * 2 + 2, be = bi;
while (coefficient > 0)
{
var c = coefficient;
coefficient /= 10;
*--bi = (char)(c - coefficient * 10 + '0');
}
exponent += (int)(be - bi - 1);
for (char* bee = MaxFormatDigits + bi; be < bee; ++be)
*be = '0';
bi--;
*bi = *(bi + 1);
*(bi + 1) = decimalMark;
if (sign)
*--bi = '-';
*be++ = 'e';
*be++ = exponent >= 0 ? '+' : '-';
{
be += 3;
for (int j = 0, exp = Math.Abs(exponent); j < 3; ++j)
{
var e = exp;
exp /= 10;
*(be - 1 - j) = (char)(e - exp * 10 + '0');
}
}
return new string(bi, 0, (int)(be - bi));
}
}
public static StringBuilder AppendTo(UInt64 value, char decimalMark, bool floatStyle, StringBuilder stringBuilder)
{
if (!IsFinite(value))
{
// Value is either Inf or NaN
// TODO: Do we need SNaN?
return stringBuilder.Append(IsNaN(value) ? "NaN" : SignBit(value) ? "-Infinity" : "Infinity");
}
Int32 partsExponent;
BID_UINT64 partsCoefficient;
if ((~value & SpecialEncodingMask) == 0) //if ((x & SpecialEncodingMask) == SpecialEncodingMask)
{
partsCoefficient = UnpackSpecial(value, out partsExponent);
}
else
{
// Extract the exponent.
partsExponent = (int)(value >> ExponentShiftSmall) & (int)ShiftedExponentMask;
// Extract the coefficient.
partsCoefficient = value & SmallCoefficientMask;
}
if (partsCoefficient == 0)
{
return stringBuilder.Append(!floatStyle ? "0" : ("0" + decimalMark + "0"));
}
int exponent = partsExponent - ExponentBias;
unsafe
{
var bufferLength = 512;
var buffer = stackalloc char[bufferLength];
if (exponent >= 0)
{
int bi = bufferLength;
if (floatStyle)
{
buffer[--bi] = '0';
buffer[--bi] = decimalMark;
}
for (int i = 0; i < exponent; ++i)
buffer[--bi] = '0';
while (partsCoefficient > 0)
{
bi = FormatUIntFromBcdTable((int)(partsCoefficient % BcdDivider), buffer, bi);
partsCoefficient /= BcdDivider;
}
while (buffer[bi] == '0')
++bi;
if (SignBit(value))
buffer[--bi] = '-';
#if NET40
char[] heapBuffer = new char[bufferLength - bi];
Marshal.Copy((IntPtr)(buffer + bi), heapBuffer, 0, heapBuffer.Length);
return stringBuilder.Append(heapBuffer);
#else
return stringBuilder.Append(buffer + bi, bufferLength - bi);
#endif
}
else
{ // exponent < 0
int bi = bufferLength;
int digits = NumberOfDigits(partsCoefficient);
if (digits + exponent > 0)
{
ulong integralPart = partsCoefficient / PowersOfTen[-exponent];
ulong fractionalPart = partsCoefficient % PowersOfTen[-exponent];
while (fractionalPart > 0)
{
bi = FormatUIntFromBcdTable((int)(fractionalPart % BcdDivider), buffer, bi);
fractionalPart /= BcdDivider;
}
int written = bufferLength - bi /* already written */;
//if (written < -exponent /* must be written */)
for (int ei = 0, ee = -exponent - written; ei < ee; ++ei)
buffer[--bi] = '0';
bi = bufferLength + exponent; /* buffer.length - (-exponent) */
buffer[--bi] = decimalMark;
while (integralPart > 0)
{
bi = FormatUIntFromBcdTable((int)(integralPart % BcdDivider), buffer, bi);
integralPart /= BcdDivider;
}
while (buffer[bi] == '0')
++bi;
if (SignBit(value))
buffer[--bi] = '-';
int be = bufferLength;
while (buffer[be - 1] == '0')
--be;
if (buffer[be - 1] == decimalMark)
{
if (!floatStyle)
{
--be;
}
else if (be < bufferLength)
{
buffer[be++] = '0';
}
}
#if NET40
char[] heapBuffer = new char[be - bi];
Marshal.Copy((IntPtr)(buffer + bi), heapBuffer, 0, heapBuffer.Length);
return stringBuilder.Append(heapBuffer);
#else
return stringBuilder.Append(buffer + bi, be - bi);
#endif
}
else
{
while (partsCoefficient > 0)
{
bi = FormatUIntFromBcdTable((int)(partsCoefficient % BcdDivider), buffer, bi);
partsCoefficient /= BcdDivider;
}
int written = bufferLength - bi /* already written */;
//if (written < -exponent /* must be written */)
for (int ei = 0, ee = -exponent - written; ei < ee; ++ei)
buffer[--bi] = '0';
bi = bufferLength + exponent; /* buffer.length - (-exponent) */
buffer[--bi] = decimalMark;
buffer[--bi] = '0';
if (SignBit(value))
buffer[--bi] = '-';
int be = bufferLength;
while (buffer[be - 1] == '0')
--be;
#if NET40
char[] heapBuffer = new char[be - bi];
Marshal.Copy((IntPtr)(buffer + bi), heapBuffer, 0, heapBuffer.Length);
return stringBuilder.Append(heapBuffer);
#else
return stringBuilder.Append(buffer + bi, be - bi);
#endif
}
}
}
#pragma warning restore CS0162
}
public static StringBuilder ScientificAppendTo(UInt64 value, char decimalMark, StringBuilder text)
{
if (IsNull(value))
return text.Append("null");
if (!IsFinite(value))
{
// Value is either Inf or NaN
// TODO: Do we need SNaN?
return text.Append(IsNaN(value) ? "NaN" : SignBit(value) ? "-Infinity" : "Infinity");
}
bool sign;
int exponent;
UInt64 coefficient = Unpack(value, out sign, out exponent);
if (coefficient == 0)
return text.Append('0').Append(decimalMark).Append("000000000000000e+000");
exponent -= BaseExponent;
unsafe
{
char* buffer = stackalloc char[MaxFormatDigits * 4];
char* bi = buffer + MaxFormatDigits * 2 + 2, be = bi;
while (coefficient > 0)
{
var c = coefficient;
coefficient /= 10;
*--bi = (char)(c - coefficient * 10 + '0');
}
exponent += (int)(be - bi - 1);
for (char* bee = MaxFormatDigits + bi; be < bee; ++be)
*be = '0';
bi--;
*bi = *(bi + 1);
*(bi + 1) = decimalMark;
if (sign)
*--bi = '-';
*be++ = 'e';
*be++ = exponent >= 0 ? '+' : '-';
{
be += 3;
for (int j = 0, exp = Math.Abs(exponent); j < 3; ++j)
{
var e = exp;
exp /= 10;
*(be - 1 - j) = (char)(e - exp * 10 + '0');
}
}
#if NET40
char[] heapBuffer = new char[(int)(be - bi)];
Marshal.Copy((IntPtr)bi, heapBuffer, 0, heapBuffer.Length);
return text.Append(heapBuffer);
#else
return text.Append(bi, (int)(be - bi));
#endif
}
}
internal static String ToDebugString(UInt64 value)
{
bool signBit = (Int64)value < 0;
Int32 exponent;
UInt64 coefficient = Unpack(value, out exponent);
StringBuilder sb = new StringBuilder(64)
.Append("0x").Append($"{value:X16}UL")
.Append("[").Append(signBit ? '-' : '+').Append(',')
.Append(exponent).Append(',').Append(coefficient).Append(']');
return sb.ToString();
}
#endregion
#region Conversion
#endregion
#region Private constants
public const UInt64 SignMask = 0x8000000000000000UL;
public const UInt64 InfinityMask = 0x7800000000000000UL;
public const UInt64 SignedInfinityMask = 0xF800000000000000UL;
public const UInt64 NaNMask = 0x7C00000000000000UL;
public const UInt64 SignalingNaNMask = 0xFC00000000000000UL;
public const UInt64 SteeringBitsMask = 0x6000000000000000UL;
public const UInt64 MaskBinarySig1 = 0x001FFFFFFFFFFFFFUL;
public const UInt64 MaskBinarySig2 = 0x0007FFFFFFFFFFFFUL;
public const UInt64 MaskBinaryOr2 = 0x0020000000000000UL;
public const UInt64 SpecialEncodingMask = 0x6000000000000000UL;
public const UInt64 LargeCoefficientMask = 0x0007FFFFFFFFFFFFUL;
public const UInt64 LargeCoefficientHighBits = 0x0020000000000000UL;
public const UInt64 SmallCoefficientMask = 0x001FFFFFFFFFFFFFUL;
public const UInt64 MinCoefficient = 0UL;
public const UInt64 MaxCoefficient = 9999999999999999UL;
public const UInt64 ShiftedExponentMask = 0x3FF;
public const Int32 ExponentShiftLarge = 51;
public const Int32 ExponentShiftSmall = 53;
public const UInt64 LargeCoefficientExponentMask = ShiftedExponentMask << ExponentShiftLarge;
public const UInt64 SmallCoefficientExponentMask = ShiftedExponentMask << ExponentShiftSmall;
public const Int32 MinExponent = -383;
public const Int32 MaxExponent = 384;
public const Int32 BiasedExponentMaxValue = 767;
public const Int32 ExponentBias = 398;
public const Int32 BaseExponent = ExponentBias;
public const Int32 MaxFormatDigits = 16;
public const Int32 BidRoundingToNearest = 0x00000;
public const Int32 BidRoundingDown = 0x00001;
public const Int32 BidRoundingUp = 0x00002;
public const Int32 BidRoundingToZero = 0x00003;
public const Int32 BidRoundingTiesAway = 0x00004;
private static readonly UInt64[,] BidRoundConstTable = new UInt64[,]
{
{ // RN
0, // 0 extra digits
5, // 1 extra digits
50, // 2 extra digits
500, // 3 extra digits
5000, // 4 extra digits
50000, // 5 extra digits
500000, // 6 extra digits
5000000, // 7 extra digits
50000000, // 8 extra digits
500000000, // 9 extra digits
5000000000, // 10 extra digits
50000000000, // 11 extra digits
500000000000, // 12 extra digits
5000000000000, // 13 extra digits
50000000000000, // 14 extra digits
500000000000000, // 15 extra digits
5000000000000000, // 16 extra digits
50000000000000000, // 17 extra digits
500000000000000000 // 18 extra digits
},
{ // RD
0, // 0 extra digits
0, // 1 extra digits
0, // 2 extra digits
0, // 3 extra digits
0, // 4 extra digits
0, // 5 extra digits
0, // 6 extra digits
0, // 7 extra digits
0, // 8 extra digits
0, // 9 extra digits
0, // 10 extra digits
0, // 11 extra digits
0, // 12 extra digits
0, // 13 extra digits
0, // 14 extra digits
0, // 15 extra digits
0, // 16 extra digits
0, // 17 extra digits
0 // 18 extra digits
},
{ // round to Inf
0, // 0 extra digits
9, // 1 extra digits
99, // 2 extra digits
999, // 3 extra digits
9999, // 4 extra digits
99999, // 5 extra digits
999999, // 6 extra digits
9999999, // 7 extra digits
99999999, // 8 extra digits
999999999, // 9 extra digits
9999999999, // 10 extra digits
99999999999, // 11 extra digits
999999999999, // 12 extra digits
9999999999999, // 13 extra digits
99999999999999, // 14 extra digits
999999999999999, // 15 extra digits
9999999999999999, // 16 extra digits
99999999999999999, // 17 extra digits
999999999999999999 // 18 extra digits
},
{ // RZ
0, // 0 extra digits
0, // 1 extra digits
0, // 2 extra digits
0, // 3 extra digits
0, // 4 extra digits
0, // 5 extra digits
0, // 6 extra digits
0, // 7 extra digits
0, // 8 extra digits
0, // 9 extra digits
0, // 10 extra digits
0, // 11 extra digits
0, // 12 extra digits
0, // 13 extra digits
0, // 14 extra digits
0, // 15 extra digits
0, // 16 extra digits
0, // 17 extra digits
0 // 18 extra digits
},
{ // round ties away from 0
0, // 0 extra digits
5, // 1 extra digits
50, // 2 extra digits
500, // 3 extra digits
5000, // 4 extra digits
50000, // 5 extra digits
500000, // 6 extra digits
5000000, // 7 extra digits
50000000, // 8 extra digits
500000000, // 9 extra digits
5000000000, // 10 extra digits
50000000000, // 11 extra digits
500000000000, // 12 extra digits
5000000000000, // 13 extra digits
50000000000000, // 14 extra digits
500000000000000, // 15 extra digits
5000000000000000, // 16 extra digits
50000000000000000, // 17 extra digits
500000000000000000 // 18 extra digits
}
};
private static readonly UInt64[,] BidReciprocals10_128 = {
{0, 0}, // 0 extra digits
{0x3333333333333334, 0x3333333333333333}, // 1 extra digit
{0x51eb851eb851eb86, 0x051eb851eb851eb8}, // 2 extra digits
{0x3b645a1cac083127, 0x0083126e978d4fdf}, // 3 extra digits
{0x4af4f0d844d013aa, 0x00346dc5d6388659}, // 10^(-4) * 2^131
{0x08c3f3e0370cdc88, 0x0029f16b11c6d1e1}, // 10^(-5) * 2^134
{0x6d698fe69270b06d, 0x00218def416bdb1a}, // 10^(-6) * 2^137
{0xaf0f4ca41d811a47, 0x0035afe535795e90}, // 10^(-7) * 2^141
{0xbf3f70834acdaea0, 0x002af31dc4611873}, // 10^(-8) * 2^144
{0x65cc5a02a23e254d, 0x00225c17d04dad29}, // 10^(-9) * 2^147
{0x6fad5cd10396a214, 0x0036f9bfb3af7b75}, // 10^(-10) * 2^151
{0xbfbde3da69454e76, 0x002bfaffc2f2c92a}, // 10^(-11) * 2^154
{0x32fe4fe1edd10b92, 0x00232f33025bd422}, // 10^(-12) * 2^157
{0x84ca19697c81ac1c, 0x00384b84d092ed03}, // 10^(-13) * 2^161
{0x03d4e1213067bce4, 0x002d09370d425736}, // 10^(-14) * 2^164
{0x3643e74dc052fd83, 0x0024075f3dceac2b}, // 10^(-15) * 2^167
{0x56d30baf9a1e626b, 0x0039a5652fb11378}, // 10^(-16) * 2^171
{0x12426fbfae7eb522, 0x002e1dea8c8da92d}, // 10^(-17) * 2^174
{0x41cebfcc8b9890e8, 0x0024e4bba3a48757}, // 10^(-18) * 2^177
{0x694acc7a78f41b0d, 0x003b07929f6da558}, // 10^(-19) * 2^181
{0xbaa23d2ec729af3e, 0x002f394219248446}, // 10^(-20) * 2^184
{0xfbb4fdbf05baf298, 0x0025c768141d369e}, // 10^(-21) * 2^187
{0x2c54c931a2c4b759, 0x003c7240202ebdcb}, // 10^(-22) * 2^191
{0x89dd6dc14f03c5e1, 0x00305b66802564a2}, // 10^(-23) * 2^194
{0xd4b1249aa59c9e4e, 0x0026af8533511d4e}, // 10^(-24) * 2^197
{0x544ea0f76f60fd49, 0x003de5a1ebb4fbb1}, // 10^(-25) * 2^201
{0x76a54d92bf80caa1, 0x00318481895d9627}, // 10^(-26) * 2^204
{0x921dd7a89933d54e, 0x00279d346de4781f}, // 10^(-27) * 2^207
{0x8362f2a75b862215, 0x003f61ed7ca0c032}, // 10^(-28) * 2^211
{0xcf825bb91604e811, 0x0032b4bdfd4d668e}, // 10^(-29) * 2^214
{0x0c684960de6a5341, 0x00289097fdd7853f}, // 10^(-30) * 2^217
{0x3d203ab3e521dc34, 0x002073accb12d0ff}, // 10^(-31) * 2^220
{0x2e99f7863b696053, 0x0033ec47ab514e65}, // 10^(-32) * 2^224
{0x587b2c6b62bab376, 0x002989d2ef743eb7}, // 10^(-33) * 2^227
{0xad2f56bc4efbc2c5, 0x00213b0f25f69892}, // 10^(-34) * 2^230
{0x0f2abc9d8c9689d1, 0x01a95a5b7f87a0ef}, // 35 extra digits
};
private static Int32[] BidRecipeScale = {
129 - 128, // 1
129 - 128, // 1/10
129 - 128, // 1/10^2
129 - 128, // 1/10^3
3, // 131 - 128
6, // 134 - 128
9, // 137 - 128
13, // 141 - 128
16, // 144 - 128
19, // 147 - 128
23, // 151 - 128
26, // 154 - 128
29, // 157 - 128
33, // 161 - 128
36, // 164 - 128
39, // 167 - 128
43, // 171 - 128
46, // 174 - 128
49, // 177 - 128
53, // 181 - 128
56, // 184 - 128
59, // 187 - 128
63, // 191 - 128
66, // 194 - 128
69, // 197 - 128
73, // 201 - 128
76, // 204 - 128
79, // 207 - 128
83, // 211 - 128
86, // 214 - 128
89, // 217 - 128
92, // 220 - 128
96, // 224 - 128
99, // 227 - 128
102, // 230 - 128
109, // 237 - 128, 1/10^35
};
#endregion
public static UInt64 Canonize(UInt64 value)
{
Int32 exponent;
Boolean isNegative = (Int64)value < 0;
// Unsigned division by constant is not optimized properly on the current version of MS .NET
// The number will not become negative in any case
Int64 mantissa;
if ((~value & SpecialEncodingMask) == 0) //if ((x & SpecialEncodingMask) == SpecialEncodingMask)
{
Int32 exp2;
mantissa = (Int64)UnpackSpecial(value, out exp2);
exponent = exp2;
}
else
{
// Extract the exponent.
exponent = (int)(value >> ExponentShiftSmall) & (int)ShiftedExponentMask;
// Extract the coefficient.
mantissa = (Int64)(value & SmallCoefficientMask);
}
if (mantissa == 0)
return Zero;
while (true)
{
Int64 c2 = mantissa / 10;
if (mantissa != c2 * 10)
break;
mantissa = c2;
++exponent;
}
return Pack(isNegative, exponent, (UInt64)mantissa, BidRoundingToNearest);
}
public static UInt64 Pack(bool isNegative, Int32 exponent, UInt64 coefficient, Int32 roundingMode)
{
UInt64 sgn = isNegative ? SignMask : 0;
UInt64 Q_low_0, Q_low_1;
UInt64 QH, r, mask, _C64, remainder_h;
int extra_digits, amount, amount2;
if (coefficient > 9999999999999999UL)
{
exponent++;
coefficient = 1000000000000000UL;
}
// Check for possible underflow/overflow.
if (exponent > BiasedExponentMaxValue || exponent < 0)
{
if (exponent < 0)
{
// Underflow.
if (exponent + MaxFormatDigits < 0)
{
if (roundingMode == BidRoundingDown && isNegative)
return 0x8000000000000001UL;
if (roundingMode == BidRoundingUp && !isNegative)
return 1L;
return sgn;
}
if (isNegative && (roundingMode == BidRoundingDown || roundingMode == BidRoundingUp))
roundingMode = 3 - roundingMode;
// Get digits to be shifted out
extra_digits = -exponent;
coefficient += BidRoundConstTable[roundingMode, extra_digits];
// Get coefficient * (2^M[extra_digits])/10^extra_digits
{
ulong ALBL_0, ALBL_1, ALBH_0, ALBH_1, QM2_0, QM2_1;
{
ulong CXH, CXL, CYH, CYL, PL, PH, PM, PM2;
CXH = coefficient >> 32;
CXL = (uint)((coefficient));
CYH = BidReciprocals10_128[extra_digits, 1] >> 32;
CYL = (uint)BidReciprocals10_128[extra_digits, 1];
PM = CXH * CYL;
PH = CXH * CYH;
PL = CXL * CYL;
PM2 = CXL * CYH;
PH += (PM >> 32);
PM = (PM & 0xFFFFFFFFL) + PM2 + (PL >> 32);
ALBH_1 = PH + (PM >> 32);
ALBH_0 = (PM << 32) + (uint)PL;
}
{
ulong CXH, CXL, CYH, CYL, PL, PH, PM, PM2;
CXH = ((coefficient)) >> 32;
CXL = (uint)((coefficient));
CYH = BidReciprocals10_128[extra_digits, 0] >> 32;
CYL = (uint)BidReciprocals10_128[extra_digits, 0];
PM = CXH * CYL;
PH = CXH * CYH;
PL = CXL * CYL;
PM2 = CXL * CYH;
PH += (PM >> 32);
PM = (PM & 0xFFFFFFFFL) + PM2 + (PL >> 32);
ALBL_1 = PH + (PM >> 32);
ALBL_0 = (PM << 32) + (PL & 0xFFFFFFFFL);
}
Q_low_0 = ALBL_0;
{
ulong R64H;
R64H = ALBH_1;
QM2_0 = ALBL_1 + ALBH_0;
if (QM2_0 < ALBL_1)
R64H++;
QM2_1 = R64H;
}
Q_low_1 = QM2_0;
QH = QM2_1;
}
// Now get P/10^extra_digits: shift Q_high right by M[extra_digits]-128
amount = BidRecipeScale[extra_digits];
_C64 = QH >> amount;
if (roundingMode == BidRoundingToNearest)
if ((_C64 & 1) != 0)
{
// Check whether fractional part of initial_P/10^extra_digits is exactly .5
// Get remainder
amount2 = 64 - amount;
remainder_h = 0;
remainder_h--;
remainder_h >>= amount2;
remainder_h = remainder_h & QH;
if (remainder_h == 0L
&& (Q_low_1 < BidReciprocals10_128[extra_digits, 1]
|| (Q_low_1 == BidReciprocals10_128[extra_digits, 1]
&& Q_low_0 < BidReciprocals10_128[extra_digits, 0])))
{
_C64--;
}
}
return sgn | _C64;
}
if (coefficient == 0L)
{
if (exponent > BiasedExponentMaxValue)
exponent = BiasedExponentMaxValue;
}
while (coefficient < 1000000000000000L && exponent >= 3 * 256)
{
exponent--;
coefficient = (coefficient << 3) + (coefficient << 1); // *= 10
}
if (exponent > BiasedExponentMaxValue)
{
// Overflow
r = sgn | InfinityMask;
if (roundingMode == BidRoundingDown)
{
if (!isNegative)
r = MaxValue;
}
else
if (roundingMode == BidRoundingToZero)
{
r = sgn | MaxValue;
}
else if (roundingMode == BidRoundingUp)
{
if (isNegative)
r = MinValue;
}
return r;
}
}
mask = 1;
mask <<= ExponentShiftSmall;
// Check whether coefficient fits in 10 * 5 + 3 bits.
if (coefficient < mask)
{
r = (ulong)exponent;
r <<= ExponentShiftSmall;
r |= (coefficient | sgn);
return r;
}
// Special format.
// Eliminate the case coefficient == 10^16 after rounding.
if (coefficient == 10000000000000000)
{
r = (ulong)(exponent + 1);
r <<= ExponentShiftSmall;
r |= (1000000000000000 | sgn);
return r;
}
r = (ulong)exponent;
r <<= ExponentShiftLarge;
r |= (sgn | SpecialEncodingMask);
// Add coefficient, without leading bits.
mask = (mask >> 2) - 1;
coefficient &= mask;
r |= coefficient;
return r;
}
public static UInt64 UnpackSpecial(UInt64 x, out Int32 exponent)
{
UInt64 coefficient;
if ((x & InfinityMask) == InfinityMask)
{
exponent = 0;
coefficient = x & 0xFE03FFFFFFFFFFFFUL;
if ((x & 0x0003FFFFFFFFFFFFUL) >= 1000000000000000UL)
coefficient = x & 0xFE00000000000000UL;
if ((x & NaNMask) == InfinityMask)
coefficient = x & SignedInfinityMask;
return 0;
}
// Extract the exponent.
exponent = (int)(x >> ExponentShiftLarge) & (int)ShiftedExponentMask;
// Check for non-canonical values.
coefficient = (x & LargeCoefficientMask) | LargeCoefficientHighBits;
if (coefficient >= 10000000000000000UL)
coefficient = 0;
return coefficient;
}
// Older version, currently unused
public static UInt64 Unpack(UInt64 x, out Boolean signBit, out Int32 exponent)
{
signBit = (Int64)x < 0;
if ((x & SpecialEncodingMask) != SpecialEncodingMask)
{
// Extract the exponent.
exponent = (int)(x >> ExponentShiftSmall) & (int)ShiftedExponentMask;
// Extract the coefficient.
return x & SmallCoefficientMask;
}
return UnpackSpecial(x, out exponent);
}
// Without sign
public static UInt64 Unpack(UInt64 x, out Int32 exponent)
{
if ((~x & SpecialEncodingMask) != 0) //if ((x & SpecialEncodingMask) != SpecialEncodingMask)
{
// Extract the exponent.
exponent = (int)(x >> ExponentShiftSmall) & (int)ShiftedExponentMask;
// Extract the coefficient.
return x & SmallCoefficientMask;
}
return UnpackSpecial(x, out exponent);
//Int32 exp2;
//UInt64 coefficient = UnpackSpecial(x, out exp2);
//exponent = exp2;
//return coefficient;
}
}
}