in csharp/EPAM.Deltix.DFP/DotNetReImpl.cs [1109:1340]
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;
}