golang / go

The Go programming language

Home Page:https://go.dev

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

math/big: incorrect string->Float conversion or printing for literals with large exponent

griesemer opened this issue · comments

package main

import (
    "fmt"
    "math/big"
)

func main() {
    var x big.Float
    x.SetString("1e81391777742999")
    fmt.Printf("x = %.6g\n", &x)
}

prints x = 1e+151 which is incorrect.

Once this is fixed, re-investigate issue #11326.

The error occurs in the scan function (in math/big/floatconv.go), specifically during the 10**exp10 correction multiplication.

On line 130 we call umul as

z.umul(z, p.pow10(exp10))

umul(x, y) has precondition

x and y must have a non-empty mantissa and valid exponent.

and there's no input checking for +Inf values. Unfortunately p.pow10(exp10) can be Inf (when exp10 is big).

When Parse is called with 1e81391777742999, this results in a call to pow10 that returns an Inf Float. Then umul is called as umul(1, +Inf) and happily returns a bogus value of 1e151.

The fix is simple: if the umul call on line 130 is replaced with a Mul call (which does Inf checking), then

x.SetString("1e81391777742999")
fmt.Printf("x = %.6g\n", &x)

correctly gives

+Inf

The fix also uncovered two wrong test. In the first one (floatconv_test.go, line 370)

{"1e1000000000", 64, 'p', 0, "0x.ecc5f45aa573d3p+1538481529"},

the expected value (with exponent 1538481529) is wrong. 1e1000000000 is not representable as a floating point number with int32 exponent. Again, what is coded as the expected result is a bogus number returned by an umul call with an Inf parameter.

Another test

{"1e-1000000000", 64, 'p', 0, "0x.8a64dd983a4c7dabp-1538481528"}

proves that negative exponents too are mis-handled. In fact, 1e-81391777742999 returns 1e-151. This problem, too, can be fixed by replacing a uquo call (no Inf checking), with a Quo, in line 128 of floatconv.go

I've sent a patch.

CL https://golang.org/cl/13778 mentions this issue.