diff options
author | matz <matz@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2005-09-16 07:22:49 +0000 |
---|---|---|
committer | matz <matz@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2005-09-16 07:22:49 +0000 |
commit | 56d82da97cd141231f72d9919def6a1ce8fd8445 (patch) | |
tree | 9ecd99dadb40c7b70055ee7eae2b7f5b9f3eb296 /ext/bigdecimal | |
parent | e0166cd8f6e8e3f29683e39ef5ff91822aa7c9d8 (diff) | |
download | ruby-56d82da97cd141231f72d9919def6a1ce8fd8445.tar.gz |
* ext/bigdecimal/bigdecimal.c: patch from "NATORI Shin"
(u-tokyo.ac.jp) applied to fix rounding bug.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@9179 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'ext/bigdecimal')
-rw-r--r-- | ext/bigdecimal/bigdecimal.c | 46 |
1 files changed, 38 insertions, 8 deletions
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; } |