csharp/EPAM.Deltix.DFP/DotNetReImpl.cs (1,129 lines of code) (raw):

using System; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Text; using BID_UINT64 = System.UInt64; using BID_UINT32 = System.UInt32; using _IDEC_flags = System.UInt32; [assembly: InternalsVisibleToAttribute("EPAM.Deltix.DFP.Test")] [assembly: InternalsVisibleToAttribute("EPAM.Deltix.DFP.Benchmark")] namespace EPAM.Deltix.DFP { internal static class DotNetReImpl { public const int BID_ROUNDING_TO_NEAREST = 0x00000; public const int BID_ROUNDING_DOWN = 0x00001; public const int BID_ROUNDING_UP = 0x00002; public const int BID_ROUNDING_TO_ZERO = 0x00003; public const int BID_ROUNDING_TIES_AWAY = 0x00004; public const int BID_ROUNDING_EXCEPTION = 0x00005; public const uint BID_EXACT_STATUS = 0x00000000; public const uint DEC_FE_INVALID = 0x01; public const uint DEC_FE_UNNORMAL = 0x02; public const uint DEC_FE_DIVBYZERO = 0x04; public const uint DEC_FE_OVERFLOW = 0x08; public const uint DEC_FE_UNDERFLOW = 0x10; public const uint DEC_FE_INEXACT = 0x20; public const uint BID_INEXACT_EXCEPTION = DEC_FE_INEXACT; public const uint BID_UNDERFLOW_EXCEPTION = DEC_FE_UNDERFLOW; public const uint BID_OVERFLOW_EXCEPTION = DEC_FE_OVERFLOW; public const uint BID_ZERO_DIVIDE_EXCEPTION = DEC_FE_DIVBYZERO; public const uint BID_DENORMAL_EXCEPTION = DEC_FE_UNNORMAL; public const uint BID_INVALID_EXCEPTION = DEC_FE_INVALID; public const uint BID_UNDERFLOW_INEXACT_EXCEPTION = (DEC_FE_UNDERFLOW | DEC_FE_INEXACT); public const uint BID_OVERFLOW_INEXACT_EXCEPTION = (DEC_FE_OVERFLOW | DEC_FE_INEXACT); public const uint BID_INVALID_FORMAT = 0x10000; public const int MAX_FORMAT_DIGITS = 16; public const int DECIMAL_EXPONENT_BIAS = 398; public const int MAX_DECIMAL_EXPONENT = 767; private static char tolower_macro(char x) { return ((x - 'A') <= ('Z' - 'A')) ? (char)(x - 'A' + 'a') : x; } public static void __set_status_flags(ref _IDEC_flags fpsc, uint status) { fpsc |= status; } public static bool is_inexact(_IDEC_flags fpsc) { return (fpsc & BID_INEXACT_EXCEPTION) != 0; } private unsafe static bool IsStrEq(char* ptr, string str) { fixed (char* fixedStr = str) { char* pStr = fixedStr; while (*ptr != '\0' && *pStr != '\0') { if (*ptr != *pStr) return false; ptr++; pStr++; } return *ptr == *pStr; } } public unsafe static BID_UINT64 bid64_from_string(string s, char decimalMark, out _IDEC_flags pfpsf, int rnd_mode = BID_ROUNDING_TO_NEAREST/*, _EXC_MASKS_PARAM _EXC_INFO_PARAM*/) { BID_UINT64 coefficient_x = 0, rounded = 0; int expon_x = 0, sgn_expon, ndigits, add_expon = 0, midpoint = 0, rounded_up = 0; int dec_expon_scale = 0; fixed (char* fixedS = s.Trim().ToLowerInvariant()) { char* ps = fixedS; if (*ps == '\0') { pfpsf = BID_INVALID_FORMAT; return 0x7c00000000000000UL; // return qNaN } // determine sign BID_UINT64 sign_x = *ps == '-' ? 0x8000000000000000UL : 0; // get next character if leading +/- sign if (*ps == '-' || *ps == '+') { ps++; if (*ps == '\0') { pfpsf = BID_INVALID_FORMAT; return 0x7c00000000000000UL; // return qNaN } } // detect special cases (INF or NaN) if (*ps != decimalMark && (*ps < '0' || *ps > '9')) { if (IsStrEq(ps, "inf") || IsStrEq(ps, "infinity")) // Infinity? { pfpsf = BID_EXACT_STATUS; return 0x7800000000000000UL | sign_x; } // return sNaN if (IsStrEq(ps, "snan")) // case insensitive check for snan { pfpsf = BID_EXACT_STATUS; return 0x7e00000000000000UL | sign_x; } if (IsStrEq(ps, "nan")) // return qNaN { pfpsf = BID_EXACT_STATUS; return 0x7c00000000000000UL | sign_x; } else // if c isn't a decimal point or a decimal digit, return NaN { pfpsf = BID_INVALID_FORMAT; return 0x7c00000000000000UL; // return qNaN } } int rdx_pt_enc = 0; int right_radix_leading_zeros = 0; // detect zero (and eliminate/ignore leading zeros) if (*ps == '0' || *ps == decimalMark) { if (*ps == decimalMark) { rdx_pt_enc = 1; ps++; } // if all numbers are zeros (with possibly 1 radix point, the number is zero // should catch cases such as: 000.0 while (*ps == '0') { ps++; // for numbers such as 0.0000000000000000000000000000000000001001, // we want to count the leading zeros if (rdx_pt_enc != 0) { right_radix_leading_zeros++; } // if this character is a radix point, make sure we haven't already // encountered one if (*ps == decimalMark) { if (rdx_pt_enc == 0) { rdx_pt_enc = 1; // if this is the first radix point, and the next character is NULL, // we have a zero if (*(ps + 1) == '\0') { pfpsf = BID_EXACT_STATUS; return DotNetImpl.Zero | sign_x; // ((BID_UINT64)(398 - right_radix_leading_zeros) << 53) | sign_x; } ps++; } else { pfpsf = BID_INVALID_FORMAT; return DotNetImpl.NaN; // 0x7c00000000000000UL | sign_x; // if 2 radix points, return NaN } } else if (*ps == '\0') { pfpsf = BID_EXACT_STATUS; return DotNetImpl.Zero | sign_x; // ((BID_UINT64)(398 - right_radix_leading_zeros) << 53) | sign_x; //pres->w[1] = 0x3040000000000000UL | sign_x; } } } char c = *ps; ndigits = 0; while ((c >= '0' && c <= '9') || c == decimalMark) { if (c >= '0' && c <= '9') { dec_expon_scale += rdx_pt_enc; ndigits++; if (ndigits <= 16) { coefficient_x = (coefficient_x << 1) + (coefficient_x << 3); coefficient_x += (BID_UINT64)(c - '0'); } else if (ndigits == 17) { // coefficient rounding switch (rnd_mode) { case BID_ROUNDING_TO_NEAREST: midpoint = (c == '5' && (coefficient_x & 1) == 0) ? 1 : 0; // if coefficient is even and c is 5, prepare to round up if // subsequent digit is nonzero // if str[MAXDIG+1] > 5, we MUST round up // if str[MAXDIG+1] == 5 and coefficient is ODD, ROUND UP! if (c > '5' || (c == '5' && (coefficient_x & 1) != 0)) { coefficient_x++; rounded_up = 1; } break; case BID_ROUNDING_DOWN: if (sign_x != 0) { coefficient_x++; rounded_up = 1; } break; case BID_ROUNDING_UP: if (sign_x == 0) { coefficient_x++; rounded_up = 1; } break; case BID_ROUNDING_TIES_AWAY: if (c >= '5') { coefficient_x++; rounded_up = 1; } break; } if (coefficient_x == 10000000000000000UL) { coefficient_x = 1000000000000000UL; add_expon = 1; } if (c > '0') rounded = 1; add_expon += 1; } else { // ndigits > 17 add_expon++; if (midpoint != 0 && c > '0') { coefficient_x++; midpoint = 0; rounded_up = 1; } if (c > '0') rounded = 1; } ps++; c = *ps; } else { if (rdx_pt_enc != 0) { pfpsf = BID_INVALID_FORMAT; return DotNetImpl.NaN; // 0x7c00000000000000UL | sign_x; // return NaN } rdx_pt_enc = 1; ps++; c = *ps; } } add_expon -= (dec_expon_scale + right_radix_leading_zeros); if (c == '\0') { pfpsf = BID_EXACT_STATUS; if (rounded != 0) __set_status_flags(ref pfpsf, BID_INEXACT_EXCEPTION); return /*fast_get_BID64_check_OF*/get_BID64(sign_x, add_expon + DECIMAL_EXPONENT_BIAS, coefficient_x, 0, ref pfpsf); } if (c != 'E' && c != 'e') { pfpsf = BID_INVALID_FORMAT; return DotNetImpl.NaN; // 0x7c00000000000000UL | sign_x; // return NaN } ps++; c = *ps; sgn_expon = (c == '-') ? 1 : 0; if (c == '-' || c == '+') { ps++; c = *ps; } if (c == '\0' || c < '0' || c > '9') { pfpsf = BID_INVALID_FORMAT; return DotNetImpl.NaN; // 0x7c00000000000000UL | sign_x; // return NaN } while ((c >= '0') && (c <= '9')) { if (expon_x < (1 << 20)) { expon_x = (expon_x << 1) + (expon_x << 3); expon_x += (int)(c - '0'); } ps++; c = *ps; } if (c != '\0') { pfpsf = BID_INVALID_FORMAT; return DotNetImpl.NaN; // 0x7c00000000000000UL | sign_x; // return NaN } if (rounded != 0) { pfpsf = BID_EXACT_STATUS; __set_status_flags(ref pfpsf, BID_INEXACT_EXCEPTION); } if (sgn_expon != 0) expon_x = -expon_x; expon_x += add_expon + DECIMAL_EXPONENT_BIAS; if (expon_x < 0) { if (rounded_up != 0) coefficient_x--; rnd_mode = 0; pfpsf = BID_EXACT_STATUS; return get_BID64_UF(sign_x, expon_x, coefficient_x, rounded, rnd_mode, ref pfpsf); } pfpsf = BID_EXACT_STATUS; return get_BID64(sign_x, expon_x, coefficient_x, rnd_mode, ref pfpsf); } } public unsafe static BID_UINT64 bid64_from_string(string s, string decimalMarks, out _IDEC_flags pfpsf, int rnd_mode = BID_ROUNDING_TO_NEAREST/*, _EXC_MASKS_PARAM _EXC_INFO_PARAM*/) { if (decimalMarks.Length == 1) return bid64_from_string(s, decimalMarks[0], out pfpsf, rnd_mode); BID_UINT64 coefficient_x = 0, rounded = 0; int expon_x = 0, sgn_expon, ndigits, add_expon = 0, midpoint = 0, rounded_up = 0; int dec_expon_scale = 0; fixed (char* fixedS = s.Trim().ToLowerInvariant()) { char* ps = fixedS; if (*ps == '\0') { pfpsf = BID_INVALID_FORMAT; return 0x7c00000000000000UL; // return qNaN } // determine sign BID_UINT64 sign_x = *ps == '-' ? 0x8000000000000000UL : 0; // get next character if leading +/- sign if (*ps == '-' || *ps == '+') { ps++; if (*ps == '\0') { pfpsf = BID_INVALID_FORMAT; return 0x7c00000000000000UL; // return qNaN } } bool cEqDot = decimalMarks.IndexOf(*ps) >= 0; // detect special cases (INF or NaN) if (!cEqDot && (*ps < '0' || *ps > '9')) { if (IsStrEq(ps, "inf") || IsStrEq(ps, "infinity")) // Infinity? { pfpsf = BID_EXACT_STATUS; return 0x7800000000000000UL | sign_x; } // return sNaN if (IsStrEq(ps, "snan")) // case insensitive check for snan { pfpsf = BID_EXACT_STATUS; return 0x7e00000000000000UL | sign_x; } if (IsStrEq(ps, "nan")) // return qNaN { pfpsf = BID_EXACT_STATUS; return 0x7c00000000000000UL | sign_x; } else // if c isn't a decimal point or a decimal digit, return NaN { pfpsf = BID_INVALID_FORMAT; return 0x7c00000000000000UL; // return qNaN } } int rdx_pt_enc = 0; int right_radix_leading_zeros = 0; // detect zero (and eliminate/ignore leading zeros) if (*ps == '0' || cEqDot) { if (cEqDot) { rdx_pt_enc = 1; ps++; } // if all numbers are zeros (with possibly 1 radix point, the number is zero // should catch cases such as: 000.0 while (*ps == '0') { ps++; // for numbers such as 0.0000000000000000000000000000000000001001, // we want to count the leading zeros if (rdx_pt_enc != 0) { right_radix_leading_zeros++; } // if this character is a radix point, make sure we haven't already // encountered one if (decimalMarks.IndexOf(*ps) >= 0) { if (rdx_pt_enc == 0) { rdx_pt_enc = 1; // if this is the first radix point, and the next character is NULL, // we have a zero if (*(ps + 1) == '\0') { pfpsf = BID_EXACT_STATUS; return DotNetImpl.Zero | sign_x; // ((BID_UINT64)(398 - right_radix_leading_zeros) << 53) | sign_x; } ps++; } else { pfpsf = BID_INVALID_FORMAT; return DotNetImpl.NaN; // 0x7c00000000000000UL | sign_x; // if 2 radix points, return NaN } } else if (*ps == '\0') { pfpsf = BID_EXACT_STATUS; return DotNetImpl.Zero | sign_x; // ((BID_UINT64)(398 - right_radix_leading_zeros) << 53) | sign_x; //pres->w[1] = 0x3040000000000000UL | sign_x; } } } char c = *ps; ndigits = 0; while ((c >= '0' && c <= '9') || (decimalMarks.IndexOf(c) >= 0)) { if (c >= '0' && c <= '9') { dec_expon_scale += rdx_pt_enc; ndigits++; if (ndigits <= 16) { coefficient_x = (coefficient_x << 1) + (coefficient_x << 3); coefficient_x += (BID_UINT64)(c - '0'); } else if (ndigits == 17) { // coefficient rounding switch (rnd_mode) { case BID_ROUNDING_TO_NEAREST: midpoint = (c == '5' && (coefficient_x & 1) == 0) ? 1 : 0; // if coefficient is even and c is 5, prepare to round up if // subsequent digit is nonzero // if str[MAXDIG+1] > 5, we MUST round up // if str[MAXDIG+1] == 5 and coefficient is ODD, ROUND UP! if (c > '5' || (c == '5' && (coefficient_x & 1) != 0)) { coefficient_x++; rounded_up = 1; } break; case BID_ROUNDING_DOWN: if (sign_x != 0) { coefficient_x++; rounded_up = 1; } break; case BID_ROUNDING_UP: if (sign_x == 0) { coefficient_x++; rounded_up = 1; } break; case BID_ROUNDING_TIES_AWAY: if (c >= '5') { coefficient_x++; rounded_up = 1; } break; } if (coefficient_x == 10000000000000000UL) { coefficient_x = 1000000000000000UL; add_expon = 1; } if (c > '0') rounded = 1; add_expon += 1; } else { // ndigits > 17 add_expon++; if (midpoint != 0 && c > '0') { coefficient_x++; midpoint = 0; rounded_up = 1; } if (c > '0') rounded = 1; } ps++; c = *ps; } else { if (rdx_pt_enc != 0) { pfpsf = BID_INVALID_FORMAT; return DotNetImpl.NaN; // 0x7c00000000000000UL | sign_x; // return NaN } rdx_pt_enc = 1; ps++; c = *ps; } } add_expon -= (dec_expon_scale + right_radix_leading_zeros); if (c == '\0') { pfpsf = BID_EXACT_STATUS; if (rounded != 0) __set_status_flags(ref pfpsf, BID_INEXACT_EXCEPTION); return /*fast_get_BID64_check_OF*/get_BID64(sign_x, add_expon + DECIMAL_EXPONENT_BIAS, coefficient_x, 0, ref pfpsf); } if (c != 'E' && c != 'e') { pfpsf = BID_INVALID_FORMAT; return DotNetImpl.NaN; // 0x7c00000000000000UL | sign_x; // return NaN } ps++; c = *ps; sgn_expon = (c == '-') ? 1 : 0; if (c == '-' || c == '+') { ps++; c = *ps; } if (c == '\0' || c < '0' || c > '9') { pfpsf = BID_INVALID_FORMAT; return DotNetImpl.NaN; // 0x7c00000000000000UL | sign_x; // return NaN } while ((c >= '0') && (c <= '9')) { if (expon_x < (1 << 20)) { expon_x = (expon_x << 1) + (expon_x << 3); expon_x += (int)(c - '0'); } ps++; c = *ps; } if (c != '\0') { pfpsf = BID_INVALID_FORMAT; return DotNetImpl.NaN; // 0x7c00000000000000UL | sign_x; // return NaN } if (rounded != 0) { pfpsf = BID_EXACT_STATUS; __set_status_flags(ref pfpsf, BID_INEXACT_EXCEPTION); } if (sgn_expon != 0) expon_x = -expon_x; expon_x += add_expon + DECIMAL_EXPONENT_BIAS; if (expon_x < 0) { if (rounded_up != 0) coefficient_x--; rnd_mode = 0; pfpsf = BID_EXACT_STATUS; return get_BID64_UF(sign_x, expon_x, coefficient_x, rounded, rnd_mode, ref pfpsf); } pfpsf = BID_EXACT_STATUS; return get_BID64(sign_x, expon_x, coefficient_x, rnd_mode, ref pfpsf); } } public const int DECIMAL_MAX_EXPON_64 = 767; //public const int DECIMAL_EXPONENT_BIAS = 398; //public const int MAX_FORMAT_DIGITS = 16; public const BID_UINT64 SPECIAL_ENCODING_MASK64 = 0x6000000000000000UL; public const BID_UINT64 INFINITY_MASK64 = 0x7800000000000000UL; public const BID_UINT64 SINFINITY_MASK64 = 0xf800000000000000UL; //public const BID_UINT64 SSNAN_MASK64 = 0xfc00000000000000UL; public const BID_UINT64 NAN_MASK64 = 0x7c00000000000000UL; //public const BID_UINT64 SNAN_MASK64 = 0x7e00000000000000UL; //public const BID_UINT64 QUIET_MASK64 = 0xfdffffffffffffffUL; public const BID_UINT64 LARGE_COEFF_MASK64 = 0x0007ffffffffffffUL; public const BID_UINT64 LARGE_COEFF_HIGH_BIT64 = 0x0020000000000000UL; public const BID_UINT64 SMALL_COEFF_MASK64 = 0x001fffffffffffffUL; public const uint EXPONENT_MASK64 = 0x3ff; public const int EXPONENT_SHIFT_LARGE64 = 51; public const int EXPONENT_SHIFT_SMALL64 = 53; public const BID_UINT64 LARGEST_BID64 = 0x77fb86f26fc0ffffUL; public const BID_UINT64 SMALLEST_BID64 = 0xf7fb86f26fc0ffffUL; //public const BID_UINT64 SMALL_COEFF_MASK128 = 0x0001ffffffffffffUL; //public const BID_UINT64 LARGE_COEFF_MASK128 = 0x00007fffffffffffUL; //public const uint EXPONENT_MASK128 = 0x3fff; //public const BID_UINT64 LARGEST_BID128_HIGH = 0x5fffed09bead87c0UL; //public const BID_UINT64 LARGEST_BID128_LOW = 0x378d8e63ffffffffUL; //public const uint SPECIAL_ENCODING_MASK32 = 0x60000000; //public const uint SINFINITY_MASK32 = 0xf8000000; //public const uint INFINITY_MASK32 = 0x78000000; //public const uint LARGE_COEFF_MASK32 = 0x007fffff; //public const uint LARGE_COEFF_HIGH_BIT32 = 0x00800000; //public const uint SMALL_COEFF_MASK32 = 0x001fffff; //public const uint EXPONENT_MASK32 = 0xff; //public const int LARGEST_BID32 = 0x77f8967f; //public const uint NAN_MASK32 = 0x7c000000; //public const uint SNAN_MASK32 = 0x7e000000; //public const uint SSNAN_MASK32 = 0xfc000000; //public const uint QUIET_MASK32 = 0xfdffffff; //public const BID_UINT64 MASK_BINARY_EXPONENT = 0x7ff0000000000000UL; //public const int BINARY_EXPONENT_BIAS = 0x3ff; //public const int UPPER_EXPON_LIMIT = 51; // // no underflow checking // public static BID_UINT64 fast_get_BID64_check_OF(BID_UINT64 sgn, int expon, BID_UINT64 coeff, int rmode, ref uint fpsc) { BID_UINT64 r, mask; if (((uint)expon) >= 3 * 256 - 1) { if ((expon == 3 * 256 - 1) && coeff == 10000000000000000UL) { expon = 3 * 256; coeff = 1000000000000000UL; } if (((uint)expon) >= 3 * 256) { while (coeff < 1000000000000000UL && expon >= 3 * 256) { expon--; coeff = (coeff << 3) + (coeff << 1); } if (expon > DECIMAL_MAX_EXPON_64) { __set_status_flags(ref fpsc, BID_OVERFLOW_EXCEPTION | BID_INEXACT_EXCEPTION); // overflow r = sgn | INFINITY_MASK64; switch (rmode) { case BID_ROUNDING_DOWN: if (sgn == 0) r = LARGEST_BID64; break; case BID_ROUNDING_TO_ZERO: r = sgn | LARGEST_BID64; break; case BID_ROUNDING_UP: // round up if (sgn != 0) r = SMALLEST_BID64; break; } return r; } } } mask = 1; mask <<= EXPONENT_SHIFT_SMALL64; // check whether coefficient fits in 10*5+3 bits if (coeff < mask) { r = (BID_UINT64)expon; r <<= EXPONENT_SHIFT_SMALL64; r |= (coeff | sgn); return r; } // special format // eliminate the case coeff==10^16 after rounding if (coeff == 10000000000000000UL) { r = (BID_UINT64)(expon + 1); r <<= EXPONENT_SHIFT_SMALL64; r |= (1000000000000000UL | sgn); return r; } r = (BID_UINT64)expon; r <<= EXPONENT_SHIFT_LARGE64; r |= (sgn | SPECIAL_ENCODING_MASK64); // add coeff, without leading bits mask = (mask >> 2) - 1; coeff &= mask; r |= coeff; return r; } public struct BID_UINT128 { public BID_UINT128(BID_UINT64 w0, BID_UINT64 w1) { this.w0 = w0; this.w1 = w1; } public BID_UINT64 w0, w1; } public static readonly BID_UINT64[,] bid_round_const_table = { { // RN 0UL, // 0 extra digits 5UL, // 1 extra digits 50UL, // 2 extra digits 500UL, // 3 extra digits 5000UL, // 4 extra digits 50000UL, // 5 extra digits 500000UL, // 6 extra digits 5000000UL, // 7 extra digits 50000000UL, // 8 extra digits 500000000UL, // 9 extra digits 5000000000UL, // 10 extra digits 50000000000UL, // 11 extra digits 500000000000UL, // 12 extra digits 5000000000000UL, // 13 extra digits 50000000000000UL, // 14 extra digits 500000000000000UL, // 15 extra digits 5000000000000000UL, // 16 extra digits 50000000000000000UL, // 17 extra digits 500000000000000000UL // 18 extra digits } , { // RD 0UL, // 0 extra digits 0UL, // 1 extra digits 0UL, // 2 extra digits 00UL, // 3 extra digits 000UL, // 4 extra digits 0000UL, // 5 extra digits 00000UL, // 6 extra digits 000000UL, // 7 extra digits 0000000UL, // 8 extra digits 00000000UL, // 9 extra digits 000000000UL, // 10 extra digits 0000000000UL, // 11 extra digits 00000000000UL, // 12 extra digits 000000000000UL, // 13 extra digits 0000000000000UL, // 14 extra digits 00000000000000UL, // 15 extra digits 000000000000000UL, // 16 extra digits 0000000000000000UL, // 17 extra digits 00000000000000000UL // 18 extra digits } , { // round to Inf 0UL, // 0 extra digits 9UL, // 1 extra digits 99UL, // 2 extra digits 999UL, // 3 extra digits 9999UL, // 4 extra digits 99999UL, // 5 extra digits 999999UL, // 6 extra digits 9999999UL, // 7 extra digits 99999999UL, // 8 extra digits 999999999UL, // 9 extra digits 9999999999UL, // 10 extra digits 99999999999UL, // 11 extra digits 999999999999UL, // 12 extra digits 9999999999999UL, // 13 extra digits 99999999999999UL, // 14 extra digits 999999999999999UL, // 15 extra digits 9999999999999999UL, // 16 extra digits 99999999999999999UL, // 17 extra digits 999999999999999999UL // 18 extra digits } , { // RZ 0UL, // 0 extra digits 0UL, // 1 extra digits 0UL, // 2 extra digits 00UL, // 3 extra digits 000UL, // 4 extra digits 0000UL, // 5 extra digits 00000UL, // 6 extra digits 000000UL, // 7 extra digits 0000000UL, // 8 extra digits 00000000UL, // 9 extra digits 000000000UL, // 10 extra digits 0000000000UL, // 11 extra digits 00000000000UL, // 12 extra digits 000000000000UL, // 13 extra digits 0000000000000UL, // 14 extra digits 00000000000000UL, // 15 extra digits 000000000000000UL, // 16 extra digits 0000000000000000UL, // 17 extra digits 00000000000000000UL // 18 extra digits } , { // round ties away from 0 0UL, // 0 extra digits 5UL, // 1 extra digits 50UL, // 2 extra digits 500UL, // 3 extra digits 5000UL, // 4 extra digits 50000UL, // 5 extra digits 500000UL, // 6 extra digits 5000000UL, // 7 extra digits 50000000UL, // 8 extra digits 500000000UL, // 9 extra digits 5000000000UL, // 10 extra digits 50000000000UL, // 11 extra digits 500000000000UL, // 12 extra digits 5000000000000UL, // 13 extra digits 50000000000000UL, // 14 extra digits 500000000000000UL, // 15 extra digits 5000000000000000UL, // 16 extra digits 50000000000000000UL, // 17 extra digits 500000000000000000UL // 18 extra digits } , }; public static BID_UINT128[] bid_reciprocals10_128 = { new BID_UINT128(0UL, 0UL) , // 0 extra digits new BID_UINT128(0x3333333333333334UL, 0x3333333333333333UL), // 1 extra digit new BID_UINT128(0x51eb851eb851eb86UL, 0x051eb851eb851eb8UL), // 2 extra digits new BID_UINT128(0x3b645a1cac083127UL, 0x0083126e978d4fdfUL), // 3 extra digits new BID_UINT128(0x4af4f0d844d013aaUL, 0x00346dc5d6388659UL), // 10^(-4) * 2^131 new BID_UINT128(0x08c3f3e0370cdc88UL, 0x0029f16b11c6d1e1UL), // 10^(-5) * 2^134 new BID_UINT128(0x6d698fe69270b06dUL, 0x00218def416bdb1aUL), // 10^(-6) * 2^137 new BID_UINT128(0xaf0f4ca41d811a47UL, 0x0035afe535795e90UL), // 10^(-7) * 2^141 new BID_UINT128(0xbf3f70834acdaea0UL, 0x002af31dc4611873UL), // 10^(-8) * 2^144 new BID_UINT128(0x65cc5a02a23e254dUL, 0x00225c17d04dad29UL), // 10^(-9) * 2^147 new BID_UINT128(0x6fad5cd10396a214UL, 0x0036f9bfb3af7b75UL), // 10^(-10) * 2^151 new BID_UINT128(0xbfbde3da69454e76UL, 0x002bfaffc2f2c92aUL), // 10^(-11) * 2^154 new BID_UINT128(0x32fe4fe1edd10b92UL, 0x00232f33025bd422UL), // 10^(-12) * 2^157 new BID_UINT128(0x84ca19697c81ac1cUL, 0x00384b84d092ed03UL), // 10^(-13) * 2^161 new BID_UINT128(0x03d4e1213067bce4UL, 0x002d09370d425736UL), // 10^(-14) * 2^164 new BID_UINT128(0x3643e74dc052fd83UL, 0x0024075f3dceac2bUL), // 10^(-15) * 2^167 new BID_UINT128(0x56d30baf9a1e626bUL, 0x0039a5652fb11378UL), // 10^(-16) * 2^171 new BID_UINT128(0x12426fbfae7eb522UL, 0x002e1dea8c8da92dUL), // 10^(-17) * 2^174 new BID_UINT128(0x41cebfcc8b9890e8UL, 0x0024e4bba3a48757UL), // 10^(-18) * 2^177 new BID_UINT128(0x694acc7a78f41b0dUL, 0x003b07929f6da558UL), // 10^(-19) * 2^181 new BID_UINT128(0xbaa23d2ec729af3eUL, 0x002f394219248446UL), // 10^(-20) * 2^184 new BID_UINT128(0xfbb4fdbf05baf298UL, 0x0025c768141d369eUL), // 10^(-21) * 2^187 new BID_UINT128(0x2c54c931a2c4b759UL, 0x003c7240202ebdcbUL), // 10^(-22) * 2^191 new BID_UINT128(0x89dd6dc14f03c5e1UL, 0x00305b66802564a2UL), // 10^(-23) * 2^194 new BID_UINT128(0xd4b1249aa59c9e4eUL, 0x0026af8533511d4eUL), // 10^(-24) * 2^197 new BID_UINT128(0x544ea0f76f60fd49UL, 0x003de5a1ebb4fbb1UL), // 10^(-25) * 2^201 new BID_UINT128(0x76a54d92bf80caa1UL, 0x00318481895d9627UL), // 10^(-26) * 2^204 new BID_UINT128(0x921dd7a89933d54eUL, 0x00279d346de4781fUL), // 10^(-27) * 2^207 new BID_UINT128(0x8362f2a75b862215UL, 0x003f61ed7ca0c032UL), // 10^(-28) * 2^211 new BID_UINT128(0xcf825bb91604e811UL, 0x0032b4bdfd4d668eUL), // 10^(-29) * 2^214 new BID_UINT128(0x0c684960de6a5341UL, 0x00289097fdd7853fUL), // 10^(-30) * 2^217 new BID_UINT128(0x3d203ab3e521dc34UL, 0x002073accb12d0ffUL), // 10^(-31) * 2^220 new BID_UINT128(0x2e99f7863b696053UL, 0x0033ec47ab514e65UL), // 10^(-32) * 2^224 new BID_UINT128(0x587b2c6b62bab376UL, 0x002989d2ef743eb7UL), // 10^(-33) * 2^227 new BID_UINT128(0xad2f56bc4efbc2c5UL, 0x00213b0f25f69892UL), // 10^(-34) * 2^230 new BID_UINT128(0x0f2abc9d8c9689d1UL, 0x01a95a5b7f87a0efUL), // 35 extra digits }; public static int[] bid_recip_scale = { 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 }; public static void __mul_64x128_full(out BID_UINT64 Ph, out BID_UINT128 Ql, BID_UINT64 A, BID_UINT128 B) { BID_UINT128 ALBL, ALBH, QM2; __mul_64x64_to_128(out ALBH, A, B.w1); __mul_64x64_to_128(out ALBL, A, B.w0); Ql.w0 = ALBL.w0; __add_128_64(out QM2, ALBH, ALBL.w1); Ql.w1 = QM2.w0; Ph = QM2.w1; } public static void __mul_64x64_to_128(out BID_UINT128 P, BID_UINT64 CX, BID_UINT64 CY) { BID_UINT64 CXH, CXL, CYH, CYL, PL, PH, PM, PM2; CXH = (CX) >> 32; CXL = (BID_UINT32)(CX); CYH = (CY) >> 32; CYL = (BID_UINT32)(CY); PM = CXH * CYL; PH = CXH * CYH; PL = CXL * CYL; PM2 = CXL * CYH; PH += (PM >> 32); PM = (BID_UINT64)((BID_UINT32)PM) + PM2 + (PL >> 32); P.w1 = PH + (PM >> 32); P.w0 = (PM << 32) + (BID_UINT32)PL; } public static void __add_128_64(out BID_UINT128 R128, BID_UINT128 A128, BID_UINT64 B64) { BID_UINT64 R64H; R64H = A128.w1; (R128).w0 = (B64) + (A128).w0; if ((R128).w0 < (B64)) R64H++; (R128).w1 = R64H; } public static void __add_carry_out(out BID_UINT64 S, out BID_UINT64 CY, BID_UINT64 X, BID_UINT64 Y) { BID_UINT64 X1 = X; S = X + Y; CY = (S < X1) ? 1UL : 0; } public static void __add_carry_in_out(out BID_UINT64 S, out BID_UINT64 CY, BID_UINT64 X, BID_UINT64 Y, BID_UINT64 CI) { BID_UINT64 X1; X1 = X + CI; S = X1 + Y; CY = ((S < X1) || (X1 < CI)) ? 1UL : 0; } // // This pack macro is used when underflow is known to occur // public static BID_UINT64 get_BID64_UF(BID_UINT64 sgn, int expon, BID_UINT64 coeff, BID_UINT64 R, int rmode, ref uint fpsc) { BID_UINT128 C128, Q_low, Stemp; BID_UINT64 _C64, remainder_h, QH, carry, CY; int extra_digits, amount, amount2; uint status; // underflow if (expon + MAX_FORMAT_DIGITS < 0) { __set_status_flags(ref fpsc, BID_UNDERFLOW_EXCEPTION | BID_INEXACT_EXCEPTION); if (rmode == BID_ROUNDING_DOWN && sgn != 0) return 0x8000000000000001UL; if (rmode == BID_ROUNDING_UP && sgn == 0) return 1UL; // result is 0 return sgn; } // 10*coeff coeff = (coeff << 3) + (coeff << 1); if (sgn != 0 && (uint)(rmode - 1) < 2) rmode = 3 - rmode; if (R != 0) coeff |= 1; // get digits to be shifted out extra_digits = 1 - expon; C128.w0 = coeff + bid_round_const_table[rmode, extra_digits]; // get coeff*(2^M[extra_digits])/10^extra_digits __mul_64x128_full(out QH, out Q_low, C128.w0, bid_reciprocals10_128[extra_digits]); // now get P/10^extra_digits: shift Q_high right by M[extra_digits]-128 amount = bid_recip_scale[extra_digits]; _C64 = QH >> amount; //__shr_128(C128, Q_high, amount); if (rmode == 0) //BID_ROUNDING_TO_NEAREST 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 == 0 && (Q_low.w1 < bid_reciprocals10_128[extra_digits].w1 || (Q_low.w1 == bid_reciprocals10_128[extra_digits].w1 && Q_low.w0 < bid_reciprocals10_128[extra_digits].w0))) { _C64--; } } if (is_inexact(fpsc)) __set_status_flags(ref fpsc, BID_UNDERFLOW_EXCEPTION); else { status = BID_INEXACT_EXCEPTION; // get remainder remainder_h = QH << (64 - amount); switch (rmode) { case BID_ROUNDING_TO_NEAREST: case BID_ROUNDING_TIES_AWAY: // test whether fractional part is 0 if (remainder_h == 0x8000000000000000UL && (Q_low.w1 < bid_reciprocals10_128[extra_digits].w1 || (Q_low.w1 == bid_reciprocals10_128[extra_digits].w1 && Q_low.w0 < bid_reciprocals10_128[extra_digits].w0))) status = BID_EXACT_STATUS; break; case BID_ROUNDING_DOWN: case BID_ROUNDING_TO_ZERO: if (remainder_h == 0 && (Q_low.w1 < bid_reciprocals10_128[extra_digits].w1 || (Q_low.w1 == bid_reciprocals10_128[extra_digits].w1 && Q_low.w0 < bid_reciprocals10_128[extra_digits].w0))) status = BID_EXACT_STATUS; break; default: // round up __add_carry_out(out Stemp.w0, out CY, Q_low.w0, bid_reciprocals10_128[extra_digits].w0); __add_carry_in_out(out Stemp.w1, out carry, Q_low.w1, bid_reciprocals10_128[extra_digits].w1, CY); if ((remainder_h >> (64 - amount)) + carry >= (((BID_UINT64)1) << amount)) status = BID_EXACT_STATUS; break; } if (status != BID_EXACT_STATUS) __set_status_flags(ref fpsc, BID_UNDERFLOW_EXCEPTION | status); } return sgn | _C64; } static void ThrowPackException(BID_UINT64 signMask, int exponentIn, BID_UINT64 coefficientIn) { throw new FormatException("The unbiasedExponent(=" + (exponentIn - DECIMAL_EXPONENT_BIAS) + ") of the value (=" + coefficientIn + ") with sign(=" + (signMask != 0 ? '-' : '+') + ") can't be saved to Decimal64 without precision loss."); } // // BID64 pack macro (general form) // public static BID_UINT64 get_BID64(BID_UINT64 sgn, int exponIn, BID_UINT64 coeffIn, int rmode, ref uint fpsc) { BID_UINT128 Stemp, Q_low; BID_UINT64 QH, r, mask, _C64, remainder_h, CY, carry; int extra_digits, amount, amount2; uint status; int expon = exponIn; BID_UINT64 coeff = coeffIn; BID_UINT64 tenDivRem = 0; bool isAnyNonZeroRem = false; while (coeff > 9999999999999999UL) { tenDivRem = coeff % 10; if (tenDivRem != 0) { if (rmode == BID_ROUNDING_EXCEPTION) ThrowPackException(sgn, exponIn, coeffIn); isAnyNonZeroRem = true; } coeff /= 10; expon++; } if (isAnyNonZeroRem) { switch (rmode) { case BID_ROUNDING_TO_NEAREST: if (tenDivRem >= 5) // Rounding away from zero coeff++; break; case BID_ROUNDING_DOWN: if (sgn != 0 /*&& isAnyNonZeroRem - already checked*/) coeff++; break; case BID_ROUNDING_UP: if (sgn == 0 /*&& isAnyNonZeroRem - already checked*/) coeff++; break; case BID_ROUNDING_TO_ZERO: break; case BID_ROUNDING_TIES_AWAY: //if (isAnyNonZeroRem) - already checked coeff++; break; case BID_ROUNDING_EXCEPTION: ThrowPackException(sgn, exponIn, coeffIn); break; default: throw new ArgumentException("Unsupported roundingMode(=" + rmode + ") value."); } } if (coeff > 9999999999999999UL) { if (rmode == BID_ROUNDING_EXCEPTION) ThrowPackException(sgn, exponIn, coeffIn); expon++; coeff = 1000000000000000UL; } // check for possible underflow/overflow if (((uint)expon) >= 3 * 256) { if (expon < 0) { // underflow if (expon + MAX_FORMAT_DIGITS < 0) { __set_status_flags(ref fpsc, BID_UNDERFLOW_EXCEPTION | BID_INEXACT_EXCEPTION); if (rmode == BID_ROUNDING_EXCEPTION) ThrowPackException(sgn, exponIn, coeffIn); if (rmode == BID_ROUNDING_DOWN && sgn != 0) return 0x8000000000000001UL; if (rmode == BID_ROUNDING_UP && sgn == 0) return 1UL; // result is 0 return sgn; } if (sgn != 0 && (uint)(rmode - 1) < 2) rmode = 3 - rmode; // get digits to be shifted out extra_digits = -expon; coeff += bid_round_const_table[rmode, extra_digits]; // get coeff*(2^M[extra_digits])/10^extra_digits __mul_64x128_full(out QH, out Q_low, coeff, bid_reciprocals10_128[extra_digits]); // now get P/10^extra_digits: shift Q_high right by M[extra_digits]-128 amount = bid_recip_scale[extra_digits]; _C64 = QH >> amount; if (rmode == 0) //BID_ROUNDING_TO_NEAREST 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 == 0 && (Q_low.w1 < bid_reciprocals10_128[extra_digits].w1 || (Q_low.w1 == bid_reciprocals10_128[extra_digits].w1 && Q_low.w0 < bid_reciprocals10_128[extra_digits].w0))) { _C64--; } } if (is_inexact(fpsc)) __set_status_flags(ref fpsc, BID_UNDERFLOW_EXCEPTION); else { status = BID_INEXACT_EXCEPTION; // get remainder remainder_h = QH << (64 - amount); switch (rmode) { case BID_ROUNDING_TO_NEAREST: case BID_ROUNDING_TIES_AWAY: // test whether fractional part is 0 if (remainder_h == 0x8000000000000000UL && (Q_low.w1 < bid_reciprocals10_128[extra_digits].w1 || (Q_low.w1 == bid_reciprocals10_128[extra_digits].w1 && Q_low.w0 < bid_reciprocals10_128[extra_digits].w0))) status = BID_EXACT_STATUS; break; case BID_ROUNDING_DOWN: case BID_ROUNDING_TO_ZERO: if (remainder_h == 0 && (Q_low.w1 < bid_reciprocals10_128[extra_digits].w1 || (Q_low.w1 == bid_reciprocals10_128[extra_digits].w1 && Q_low.w0 < bid_reciprocals10_128[extra_digits].w0))) status = BID_EXACT_STATUS; break; default: // round up __add_carry_out(out Stemp.w0, out CY, Q_low.w0, bid_reciprocals10_128[extra_digits].w0); __add_carry_in_out(out Stemp.w1, out carry, Q_low.w1, bid_reciprocals10_128[extra_digits].w1, CY); if ((remainder_h >> (64 - amount)) + carry >= (((BID_UINT64)1) << amount)) status = BID_EXACT_STATUS; break; } if (status != BID_EXACT_STATUS) __set_status_flags(ref fpsc, BID_UNDERFLOW_EXCEPTION | status); } return sgn | _C64; } if (coeff == 0) { if (expon > DECIMAL_MAX_EXPON_64) expon = DECIMAL_MAX_EXPON_64; } while (coeff < 1000000000000000UL && expon >= 3 * 256) { expon--; coeff = (coeff << 3) + (coeff << 1); } if (expon > DECIMAL_MAX_EXPON_64) { __set_status_flags(ref fpsc, BID_OVERFLOW_EXCEPTION | BID_INEXACT_EXCEPTION); // overflow r = sgn | INFINITY_MASK64; switch (rmode) { case BID_ROUNDING_EXCEPTION: ThrowPackException(sgn, exponIn, coeffIn); break; case BID_ROUNDING_DOWN: if (sgn == 0) r = LARGEST_BID64; break; case BID_ROUNDING_TO_ZERO: r = sgn | LARGEST_BID64; break; case BID_ROUNDING_UP: // round up if (sgn != 0) r = SMALLEST_BID64; break; } return r; } } mask = 1; mask <<= EXPONENT_SHIFT_SMALL64; // check whether coefficient fits in 10*5+3 bits if (coeff < mask) { r = (BID_UINT64)expon; r <<= EXPONENT_SHIFT_SMALL64; r |= (coeff | sgn); return r; } // special format // eliminate the case coeff==10^16 after rounding if (coeff == 10000000000000000UL) { r = (BID_UINT64)(expon + 1); r <<= EXPONENT_SHIFT_SMALL64; r |= (1000000000000000UL | sgn); return r; } r = (BID_UINT64)expon; r <<= EXPONENT_SHIFT_LARGE64; r |= (sgn | SPECIAL_ENCODING_MASK64); // add coeff, without leading bits mask = (mask >> 2) - 1; coeff &= mask; r |= coeff; return r; } public static BID_UINT64 unpack_BID64(out BID_UINT64 psign_x, out int pexponent_x, out BID_UINT64 pcoefficient_x, BID_UINT64 x) { BID_UINT64 tmp, coeff; psign_x = x & 0x8000000000000000UL; if ((x & SPECIAL_ENCODING_MASK64) == SPECIAL_ENCODING_MASK64) { // special encodings // coefficient coeff = (x & LARGE_COEFF_MASK64) | LARGE_COEFF_HIGH_BIT64; if ((x & INFINITY_MASK64) == INFINITY_MASK64) { pexponent_x = 0; pcoefficient_x = x & 0xfe03ffffffffffffUL; if ((x & 0x0003ffffffffffffUL) >= 1000000000000000UL) pcoefficient_x = x & 0xfe00000000000000UL; if ((x & NAN_MASK64) == INFINITY_MASK64) pcoefficient_x = x & SINFINITY_MASK64; return 0; // NaN or Infinity } // check for non-canonical values if (coeff >= 10000000000000000UL) coeff = 0; pcoefficient_x = coeff; // get exponent tmp = x >> EXPONENT_SHIFT_LARGE64; pexponent_x = (int)(tmp & EXPONENT_MASK64); return coeff; } // exponent tmp = x >> EXPONENT_SHIFT_SMALL64; pexponent_x = (int)(tmp & EXPONENT_MASK64); // coefficient pcoefficient_x = (x & SMALL_COEFF_MASK64); return pcoefficient_x; } } }