This project is closed and read-only.
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
|
||