Backport #3803 » ruby_bigdecimal_round_half.patch
ext/bigdecimal/bigdecimal.c (working copy) | ||
---|---|---|
*
|
||
* ROUND_UP:: round away from zero
|
||
* ROUND_DOWN:: round towards zero (truncate)
|
||
* ROUND_HALF_UP:: round up if the appropriate digit >= 5, otherwise truncate (default)
|
||
* ROUND_HALF_DOWN:: round up if the appropriate digit >= 6, otherwise truncate
|
||
* ROUND_HALF_EVEN:: round towards the even neighbor (Banker's rounding)
|
||
* ROUND_HALF_UP:: round towards the nearest neighbor, unless both neighbors are equidistant, in which case round away from zero. (default)
|
||
* ROUND_HALF_DOWN:: round towards the nearest neighbor, unless both neighbors are equidistant, in which case round towards zero.
|
||
* ROUND_HALF_EVEN:: round towards the nearest neighbor, unless both neighbors are equidistant, in which case round towards the even neighbor (Banker's rounding)
|
||
* ROUND_CEILING:: round towards positive infinity (ceil)
|
||
* ROUND_FLOOR:: round towards negative infinity (floor)
|
||
*
|
||
... | ... | |
{
|
||
/* fracf: any positive digit under rounding position? */
|
||
/* exptoadd: number of digits needed to compensate negative nf */
|
||
int fracf;
|
||
int fracf, fracf_1further;
|
||
ssize_t n,i,ix,ioffset, exptoadd;
|
||
BDIGIT v, shifter;
|
||
BDIGIT div;
|
||
... | ... | |
n = (ssize_t)BASE_FIG - ioffset - 1;
|
||
for (shifter=1,i=0; i<n; ++i) shifter *= 10;
|
||
fracf = (v % (shifter * 10) > 0);
|
||
fracf_1further = ((v % shifter) > 0);
|
||
v /= shifter;
|
||
div = v / 10;
|
||
v = v - div*10;
|
||
if (fracf == 0) {
|
||
for (i=ix+1; (size_t)i < y->Prec; i++) {
|
||
if (y->frac[i] % BASE) {
|
||
fracf = 1;
|
||
break;
|
||
}
|
||
for (i=ix+1; (size_t)i < y->Prec; i++) {
|
||
if (y->frac[i] % BASE) {
|
||
fracf = fracf_1further = 1;
|
||
break;
|
||
}
|
||
}
|
||
memset(y->frac+ix+1, 0, (y->Prec - (ix+1)) * sizeof(BDIGIT));
|
||
... | ... | |
if (v>=5) ++div;
|
||
break;
|
||
case VP_ROUND_HALF_DOWN: /* Round half down */
|
||
if (v>=6) ++div;
|
||
if (v>5 || (v == 5 && fracf_1further)) ++div;
|
||
break;
|
||
case VP_ROUND_CEIL: /* ceil */
|
||
if (fracf && (VpGetSign(y)>0)) ++div;
|
||
... | ... | |
case VP_ROUND_HALF_EVEN: /* Banker's rounding */
|
||
if (v>5) ++div;
|
||
else if (v==5) {
|
||
if ((size_t)i == (BASE_FIG-1)) {
|
||
if (ix && (y->frac[ix-1]%2)) ++div;
|
||
if (fracf_1further) {
|
||
++div;
|
||
}
|
||
else {
|
||
if (div%2) ++div;
|
||
else {
|
||
if (ioffset == 0) {
|
||
if (ix && (y->frac[ix-1]%2)) ++div;
|
||
}
|
||
else {
|
||
if (div%2) ++div;
|
||
}
|
||
}
|
||
}
|
||
break;
|
test/bigdecimal/test_bigdecimal.rb (working copy) | ||
---|---|---|
assert_equal(3, x.round(0, BigDecimal::ROUND_CEILING))
|
||
assert_equal(2, x.round(0, BigDecimal::ROUND_FLOOR))
|
||
assert_raise(TypeError) { x.round(0, 256) }
|
||
15.times do |n|
|
||
x = BigDecimal.new("5#{'0'*n}1")
|
||
assert_equal(10**(n+2), x.round(-(n+2), BigDecimal::ROUND_HALF_DOWN))
|
||
assert_equal(10**(n+2), x.round(-(n+2), BigDecimal::ROUND_HALF_EVEN))
|
||
x = BigDecimal.new("0.5#{'0'*n}1")
|
||
assert_equal(1, x.round(0, BigDecimal::ROUND_HALF_DOWN))
|
||
assert_equal(1, x.round(0, BigDecimal::ROUND_HALF_EVEN))
|
||
x = BigDecimal.new("-0.5#{'0'*n}1")
|
||
assert_equal(-1, x.round(0, BigDecimal::ROUND_HALF_DOWN))
|
||
assert_equal(-1, x.round(0, BigDecimal::ROUND_HALF_EVEN))
|
||
end
|
||
end
|
||
def test_truncate
|