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

spec: Different results when using float64 constant vs float64(int) cast #23876

Closed
klokare opened this issue Feb 16, 2018 · 5 comments
Closed

Comments

@klokare
Copy link

klokare commented Feb 16, 2018

Please answer these questions before submitting your issue. Thanks!

What version of Go are you using (go version)?

go version go1.9.4 darwin/amd64

Does this issue reproduce with the latest release?

yes. I can replicated this on play.golang.org

What operating system and processor architecture are you using (go env)?

GOARCH="amd64"
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOOS="darwin"

What did you do?

https://play.golang.org/p/o3I21mRFPt2

package main

import (
	"fmt"
)

func main() {
	fmt.Printf("CASE A: %0.20f\n", (1.0 + (4.0 * (1.0/7.0))/2.0) / 2.0)
	fmt.Printf("CASE B: %0.20f\n", (1.0 + (float64(4) * (1.0/7.0))/2.0) / 2.0)
	
	x := 4.0
	y := float64(4)
	fmt.Printf("CASE C: %0.20f\n", (1.0 + (x * (1.0/7.0))/2.0) / 2.0)
	fmt.Printf("CASE D: %0.20f\n", (1.0 + (y * (1.0/7.0))/2.0) / 2.0)
}
CASE A: 0.64285714285714290472
CASE B: 0.64285714285714279370
CASE C: 0.64285714285714279370
CASE D: 0.64285714285714279370

What did you expect to see?

All cases should be equal

What did you see instead?

Case A is different. When using 4.0 vs float64(4) directly, I get different results (Case A vs Case B). If I assign the values to variables then I get the same result (Case C and D are the same result as Case B). There appears to be some optimisation that is affecting the 4.0 constant.

@cznic
Copy link
Contributor

cznic commented Feb 16, 2018

There appears to be some optimisation that is affecting the 4.0 constant.

Precision, not optimisation is the culprit:

Represent floating-point constants, including the parts of a complex constant, with a mantissa of at least 256 bits and a signed binary exponent of at least 16 bits.

@klokare
Copy link
Author

klokare commented Feb 16, 2018

After some good discussions on Slack (thanks everyone), this may be the correct albeit unexpected (to me and to a casual reader of the code): https://www.ardanlabs.com/blog/2014/04/introduction-to-numeric-constants-in-go.html

Constants Are Mathematically Exact
Regardless of the implementation, constants are always considered to be mathematically exact. This is something that makes constants in Go unique. This is not the case in other languages like C and C++.

@cznic, you are correct in that its precision but the issues is deeper than just the typical issues of floating point precision. it's related, it seems, to "[in] your issue all the computations are done at the "ideal constant" level with effectively unlimited precision, and then afterwards converted down to a float64" (credit @dgryski with that phrasing).

I'm left wondering about all the times I got this wrong. A typical use case for me would be using float64 constants in variable declarations à la:

x := Foo {
    Mean: (x0 + x1 + x3 + x4) / 4.0,
}

From my new understanding, that is not the same as

cnt := float64(4)
x := Foo {
    Mean: (x0 + x1 + x3 + x4) / cnt,
}

@dgryski
Copy link
Contributor

dgryski commented Feb 16, 2018

In this case, there are no other calculations to be done with the ideal 4 before it's needed as a float64 for the division, so it ends up being the same.

@klokare
Copy link
Author

klokare commented Feb 16, 2018

yeah, it was a bad example case as, in this case, the answers are the same. But introducing more complex math, like the original post, shows how that assumptions in that last example break down. Just one more thing to keep in mind. Your explanation about the "ideal 4" helps with dispelling the incorrect assumptions. Thanks.

@bradfitz
Copy link
Contributor

Sounds like there's nothing to do here. Closing.

@mikioh mikioh changed the title Different results when using float64 constant vs float64(int) cast spec: Different results when using float64 constant vs float64(int) cast Feb 21, 2018
@golang golang locked and limited conversation to collaborators Feb 21, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

5 participants