Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

math: Exp(1.0) returns different values on amd64 vs arm64 #20319

Closed
tamird opened this issue May 10, 2017 · 10 comments
Closed

math: Exp(1.0) returns different values on amd64 vs arm64 #20319

tamird opened this issue May 10, 2017 · 10 comments
Labels
FrozenDueToAge NeedsFix The path to resolution is known, but the work has not been done. release-blocker
Milestone

Comments

@tamird
Copy link
Contributor

tamird commented May 10, 2017

foo.go:

package main

import (
	"fmt"
	"math"
)

func main() {
	fmt.Println(math.Exp(1.0))
}

prints:

amd64: 2.718281828459045
arm64: 2.7182818284590455

amd64:

$ 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

arm64:

$ 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 cockroachdb/cockroach#14405.

@randall77
Copy link
Contributor

@griesemer

@ALTree
Copy link
Member

ALTree commented May 10, 2017

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.

@ianlancetaylor
Copy link
Contributor

CC @cldorian

@ALTree
Copy link
Member

ALTree commented May 10, 2017

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.

@dr2chase
Copy link
Contributor

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.

@ALTree ALTree added the NeedsDecision Feedback is required from experts, contributors, and/or the community before a change can be made. label May 30, 2017
@ALTree ALTree added this to the Go1.10 milestone May 30, 2017
@rsc
Copy link
Contributor

rsc commented Jun 5, 2017

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.

@rsc
Copy link
Contributor

rsc commented Jun 5, 2017

#6794 is what I meant.

@rsc
Copy link
Contributor

rsc commented Jun 26, 2017

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.

@rsc rsc added NeedsFix The path to resolution is known, but the work has not been done. and removed NeedsDecision Feedback is required from experts, contributors, and/or the community before a change can be made. labels Jun 26, 2017
@crvv
Copy link
Contributor

crvv commented Jul 14, 2017

  1. The issue comes from difference between Go code and AMD64 assembly code. Result of 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.
  2. A more precise value of Exp(1) is 2.71828182845904523536, and ...45 is the most precise float64 value.
  3. As said in comment of src/math/exp.go, the Go code is a simplified version of the original C(FreeBSD's /usr/src/lib/msun/src/e_exp.c). I tested the origin C code and its result is also ...455.
  4. The algorithm uses a polynomial to get the approximate value of Exp(x).
  5. If change 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.
  6. Maybe this issue should be fixed by changing 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?

@gopherbot
Copy link

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

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
FrozenDueToAge NeedsFix The path to resolution is known, but the work has not been done. release-blocker
Projects
None yet
Development

No branches or pull requests

9 participants