foo.go:
package main
import (
"fmt"
"math"
)
func main() {
fmt.Println(math.Exp(1.0))
}
prints:
amd64: 2.718281828459045
arm64: 2.7182818284590455
$ go version
go version go1.8.1 darwin/amd64
$ go env
GOARCH="amd64"
GOBIN=""
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOOS="darwin"
GOPATH="/Users/tamird/src/go"
GORACE=""
GOROOT="/Users/tamird/src/go1.8"
GOTOOLDIR="/Users/tamird/src/go1.8/pkg/tool/darwin_amd64"
GCCGO="gccgo"
CC="clang"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/sw/d6165ghx7jx3cdl7cggxzbx00000gn/T/go-build154970439=/tmp/go-build -gno-record-gcc-switches -fno-common"
CXX="clang++"
CGO_ENABLED="1"
PKG_CONFIG="pkg-config"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
$ go run foo.go
2.718281828459045
$ go version
go version go1.8.1 linux/arm64
$ go env
GOARCH="arm64"
GOBIN=""
GOEXE=""
GOHOSTARCH="arm64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/root/go"
GORACE=""
GOROOT="/usr/lib/go-1.8"
GOTOOLDIR="/usr/lib/go-1.8/pkg/tool/linux_arm64"
GCCGO="gccgo"
CC="gcc"
GOGCCFLAGS="-fPIC -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build383633414=/tmp/go-build -gno-record-gcc-switches"
CXX="g++"
CGO_ENABLED="1"
PKG_CONFIG="pkg-config"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
$ go run foo.go
2.7182818284590455
There may be other inconsistencies lurking. Discovered in https://github.com/cockroachdb/cockroach/issues/14405.
@griesemer
amd64 and s390x have assembly implementations, all the others architectures use a pure go routine and they implement different algorithms, so last-bit-mismatches are not unexpected.
CC @cldorian
Also looking at #18354 seems like the consensus is that we don't want to promise that floating point functions will return exactly the same result on all platforms. The current status is that in many cases not all bits match between different platforms for many math routines.
That's not a happy-making consensus. We've had good portable implementations of transcendentals (fdlibm) for decades now, and the same for FP<->string conversion for nearly that long.
We don't promise that the transcendentals are last-bit correct, especially for boundary cases. There is an open bug for Sin(2^80) or something like that. (We do promise that the float/string conversions are.) I admit that Exp(1) is not that much of a boundary case though. If there's a cheap improvement to one or the other to get the correctly rounded math.E out, that's fine. We're not looking for whole new algorithms for last-bit precise implementations though.
As noted above, while we're not guaranteeing last-bit correct for all inputs, I'm willing to assert that Exp(1) should be as precise as possible. So this seems reasonable as NeedsFix.
Exp(1) in Go is ...455 and result of Exp(1) in AMD64 assembly is ...45. It is irrelevant to ARM64. Use GOARCH=386 go build on AMD64 machine and result will be ...455.Exp(1) is 2.71828182845904523536, and ...45 is the most precise float64 value....455.Exp(x).P1(the first polynomial coefficient) to 1.0 / 6.0(the origin value in code is 0.166666666666666019037, float64 value is ...66602, 1.0 / 6.0 is ...66666), result of Exp(1) will be ...45. I did some very simple error statistic and can't observe difference introduced by using 1.0 / 6.0 as P1.P1 to 1.0 / 6.0 in Go code but I am not sure this is the right way. Should we do some professional error statistic and how to do that?CL https://golang.org/cl/49294 mentions this issue.
Most helpful comment
Also looking at #18354 seems like the consensus is that we don't want to promise that floating point functions will return exactly the same result on all platforms. The current status is that in many cases not all bits match between different platforms for many
mathroutines.