cmd/gc: fix floating point rounding again

Passes the expanded test in CL 100660044,
which gives me some confidence that it
might be right.

(The old code failed by not considering all the
low bits.)

LGTM=r
R=golang-codereviews, r, bradfitz
CC=golang-codereviews, iant, khr
https://golang.org/cl/99410051
This commit is contained in:
Russ Cox 2014-05-21 17:11:52 -04:00
parent 4464ae280f
commit 1be479df92
1 changed files with 22 additions and 24 deletions

View File

@ -203,7 +203,7 @@ static double
mpgetfltN(Mpflt *a, int prec, int bias) mpgetfltN(Mpflt *a, int prec, int bias)
{ {
int s, i, e, minexp; int s, i, e, minexp;
uvlong v, vm; uvlong v;
double f; double f;
if(a->val.ovf && nsavederrors+nerrors == 0) if(a->val.ovf && nsavederrors+nerrors == 0)
@ -226,25 +226,23 @@ mpgetfltN(Mpflt *a, int prec, int bias)
return 0; return 0;
} }
// pick up the mantissa and a rounding bit in a uvlong // pick up the mantissa, a rounding bit, and a tie-breaking bit in a uvlong
s = prec+1; s = prec+2;
v = 0; v = 0;
for(i=Mpnorm-1; s>=Mpscale; i--) { for(i=Mpnorm-1; s>=Mpscale; i--) {
v = (v<<Mpscale) | a->val.a[i]; v = (v<<Mpscale) | a->val.a[i];
s -= Mpscale; s -= Mpscale;
} }
vm = v; if(s > 0) {
if(s > 0)
vm = (vm<<s) | (a->val.a[i]>>(Mpscale-s));
// continue with 64 more bits
s += 64;
for(; s>=Mpscale; i--) {
v = (v<<Mpscale) | a->val.a[i];
s -= Mpscale;
}
if(s > 0)
v = (v<<s) | (a->val.a[i]>>(Mpscale-s)); v = (v<<s) | (a->val.a[i]>>(Mpscale-s));
if((a->val.a[i]&((1<<(Mpscale-s))-1)) != 0)
v |= 1;
i--;
}
for(; i >= 0; i--) {
if(a->val.a[i] != 0)
v |= 1;
}
// gradual underflow // gradual underflow
e = Mpnorm*Mpscale + a->exp - prec; e = Mpnorm*Mpscale + a->exp - prec;
@ -253,23 +251,23 @@ mpgetfltN(Mpflt *a, int prec, int bias)
s = minexp - e; s = minexp - e;
if(s > prec+1) if(s > prec+1)
s = prec+1; s = prec+1;
v |= vm & ((1ULL<<s) - 1); if((v & ((1<<s)-1)) != 0)
vm >>= s; v |= 1<<s;
v >>= s;
e = minexp; e = minexp;
} }
// round to even
v |= (v&4)>>2;
v += v&1;
v >>= 2;
//print("vm=%.16llux v=%.16llux\n", vm, v); f = (double)(v);
// round toward even
if(v != 0 || (vm&2ULL) != 0)
vm = (vm>>1) + (vm&1ULL);
else
vm >>= 1;
f = (double)(vm);
f = ldexp(f, e); f = ldexp(f, e);
if(a->val.neg) if(a->val.neg)
f = -f; f = -f;
return f; return f;
} }