Index: ext/bigdecimal/bigdecimal.c =================================================================== --- ext/bigdecimal/bigdecimal.c (revision 29252) +++ ext/bigdecimal/bigdecimal.c (working copy) @@ -308,9 +308,9 @@ * * 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) * @@ -4560,7 +4560,7 @@ { /* 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; @@ -4588,15 +4588,14 @@ n = (ssize_t)BASE_FIG - ioffset - 1; for (shifter=1,i=0; i 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)); @@ -4610,7 +4609,7 @@ 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; @@ -4621,11 +4620,16 @@ 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; Index: test/bigdecimal/test_bigdecimal.rb =================================================================== --- test/bigdecimal/test_bigdecimal.rb (revision 29252) +++ test/bigdecimal/test_bigdecimal.rb (working copy) @@ -612,6 +612,18 @@ 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