diff options
-rw-r--r-- | ChangeLog | 5 | ||||
-rw-r--r-- | ext/bigdecimal/bigdecimal.c | 46 |
2 files changed, 43 insertions, 8 deletions
@@ -1834,6 +1834,11 @@ Sat Jun 25 11:37:00 2005 NARUSE, Yui <naruse@ruby-lang.org> conv, {eucjp, shiftjis, utf8}? * ext/nkf/lib/kconv.rb: add aliases Kconv.to_* and String#to_* +Fri Jun 24 17:00:00 2005 Shigeo Kobayashi <shigeo@tinyforest.jp> + + * ext/bigdecimal/bigdecimal.c: patch from "NATORI Shin" + (u-tokyo.ac.jp) applied to fix rounding bug. + Fri Jun 24 13:17:45 2005 akira yamada <akira@ruby-lang.org> * lib/uri/common.rb, lib/uri/generic.rb: fixed typo in documents and diff --git a/ext/bigdecimal/bigdecimal.c b/ext/bigdecimal/bigdecimal.c index 5ac81114b3..7f3f1520ee 100644 --- a/ext/bigdecimal/bigdecimal.c +++ b/ext/bigdecimal/bigdecimal.c @@ -3723,28 +3723,45 @@ VpMidRound(Real *y, int f, int nf) * nf: digit location to round from the the decimal point. */ { - int n,i,ix,ioffset; - U_LONG v; + /* fracf: any positive digit under rounding position? */ + /* exptoadd: number of digits needed to compensate negative nf */ + int n,i,ix,ioffset,fracf,exptoadd; + U_LONG v,shifter; U_LONG div; nf += y->exponent*((int)BASE_FIG); + exptoadd=0; + if (nf < 0) { + exptoadd = -nf; + nf = 0; + } /* ix: x->fraq[ix] contains round position */ ix = nf/(int)BASE_FIG; - if(ix<0 || ((U_LONG)ix)>=y->Prec) return 0; /* Unable to round */ + if(((U_LONG)ix)>=y->Prec) return 0; /* Unable to round */ ioffset = nf - ix*((int)BASE_FIG); - memset(y->frac+ix+1, 0, (y->Prec - (ix+1)) * sizeof(U_LONG)); v = y->frac[ix]; /* drop digits after pointed digit */ n = BASE_FIG - ioffset - 1; - for(i=0;i<n;++i) v /= 10; + for(shifter=1,i=0;i<n;++i) shifter *= 10; + fracf = (v%(shifter*10) > 0); + v /= shifter; div = v/10; v = v - div*10; + if (fracf == 0) { + for(i=ix+1;i<y->Prec;i++) { + if (y->frac[i]%BASE) { + fracf = 1; + break; + } + } + } + memset(y->frac+ix+1, 0, (y->Prec - (ix+1)) * sizeof(U_LONG)); switch(f) { case VP_ROUND_DOWN: /* Truncate */ break; case VP_ROUND_UP: /* Roundup */ - if(v) ++div; + if(fracf) ++div; break; case VP_ROUND_HALF_UP: /* Round half up */ if(v>=5) ++div; @@ -3753,10 +3770,10 @@ VpMidRound(Real *y, int f, int nf) if(v>=6) ++div; break; case VP_ROUND_CEIL: /* ceil */ - if(v && (VpGetSign(y)>0)) ++div; + if(fracf && (VpGetSign(y)>0)) ++div; break; case VP_ROUND_FLOOR: /* floor */ - if(v && (VpGetSign(y)<0)) ++div; + if(fracf && (VpGetSign(y)<0)) ++div; break; case VP_ROUND_HALF_EVEN: /* Banker's rounding */ if(v>5) ++div; @@ -3776,13 +3793,26 @@ VpMidRound(Real *y, int f, int nf) VpRdup(y,ix); } else { S_INT s = VpGetSign(y); + int e = y->exponent; VpSetOne(y); VpSetSign(y,s); + y->exponent = e+1; } } else { y->frac[ix] = div; VpNmlz(y); } + if (exptoadd > 0) { + y->exponent += exptoadd/BASE_FIG; + exptoadd %= BASE_FIG; + for(i=0;i<exptoadd;i++) { + y->frac[0] *= 10; + if (y->frac[0] >= BASE) { + y->frac[0] /= BASE; + y->exponent++; + } + } + } return 1; } |