From: Robert Griesemer Date: Fri, 22 May 2015 00:00:37 +0000 (-0700) Subject: math/big: fix Float.Float32 conversion for denormal corner cases X-Git-Tag: go1.5beta1~493 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=79afb43a0a42abb435c9a7596d66759eb7af18f2;p=gostls13.git math/big: fix Float.Float32 conversion for denormal corner cases The existing code was incorrect for numbers that after rounding would become the smallest denormal float32 (instead the result was 0). This caused all.bash to fail if Float32() were used in the compiler for constant arithmetic (there's currently a work-around - see also issue 10321. This change fixes the implementation of Float.Float32 and adds corresponding test cases. Float32 and Float64 diverge at this point. For ease of review, this change only fixes Float32. Float64 will be made to match in a subsequent change. Fixes #10321. Change-Id: Iccafe37c1593a4946bc552e4ad2045f69be62d80 Reviewed-on: https://go-review.googlesource.com/10350 Reviewed-by: Alan Donovan --- diff --git a/src/math/big/float.go b/src/math/big/float.go index d46c046e67..b666b9dd38 100644 --- a/src/math/big/float.go +++ b/src/math/big/float.go @@ -853,9 +853,6 @@ func (x *Float) Int64() (int64, Accuracy) { panic("unreachable") } -// TODO(gri) Float32 and Float64 are very similar internally but for the -// floatxx parameters and some conversions. Should factor out shared code. - // Float32 returns the float32 value nearest to x. If x is too small to be // represented by a float32 (|x| < math.SmallestNonzeroFloat32), the result // is (0, Below) or (-0, Above), respectively, depending on the sign of x. @@ -880,64 +877,70 @@ func (x *Float) Float32() (float32, Accuracy) { emax = bias // 127 largest unbiased exponent (normal) ) - // Float mantissae m have an explicit msb and are in the range 0.5 <= m < 1.0. - // floatxx mantissae have an implicit msb and are in the range 1.0 <= m < 2.0. - // For a given mantissa m, we need to add 1 to a floatxx exponent to get the - // corresponding Float exponent. - // (see also implementation of math.Ldexp for similar code) - - if x.exp < dmin+1 { - // underflow - if x.neg { - var z float32 - return -z, Above + // Float mantissa m is 0.5 <= m < 1.0; compute exponent for floatxx mantissa. + e := x.exp - 1 // exponent for mantissa m with 1.0 <= m < 2.0 + p := mbits + 1 // precision of normal float + + // If the exponent is too small, we may have a denormal number + // in which case we have fewer mantissa bits available: reduce + // precision accordingly. + if e < emin { + p -= emin - int(e) + // Make sure we have at least 1 bit so that we don't + // lose numbers rounded up to the smallest denormal. + if p < 1 { + p = 1 } - return 0.0, Below } - // x.exp >= dmin+1 + // round var r Float - r.prec = mbits + 1 // +1 for implicit msb - if x.exp < emin+1 { - // denormal number - round to fewer bits - r.prec = uint32(x.exp - dmin) - } + r.prec = uint32(p) r.Set(x) + e = r.exp - 1 // Rounding may have caused r to overflow to ±Inf // (rounding never causes underflows to 0). if r.form == inf { - r.exp = emax + 2 // cause overflow below + e = emax + 1 // cause overflow below } - if r.exp > emax+1 { + // If the exponent is too large, overflow to ±Inf. + if e > emax { // overflow if x.neg { return float32(math.Inf(-1)), Below } return float32(math.Inf(+1)), Above } - // dmin+1 <= r.exp <= emax+1 - var s uint32 - if r.neg { - s = 1 << (fbits - 1) + // Determine sign, biased exponent, and mantissa. + var sign, bexp, mant uint32 + if x.neg { + sign = 1 << (fbits - 1) } - m := high32(r.mant) >> ebits & (1<> (fbits - r.prec) + } else { + // normal number: emin <= e <= emax + bexp = uint32(e+bias) << mbits + mant = high32(r.mant) >> ebits & (1<