Why do floating point operations yield different results in go
floating-point, go, ieee-754, precision
Solution
Constants:
- Numeric constants represent values of arbitrary precision and do not overflow.
- Represent integer constants with at least 256 bits.
- Represent floating-point constants, including the parts of a complex constant, with a mantissa of at least 256 bits and a signed exponent of at least 32 bits.
Yes, by the compiler for compile time constants.
Yes, they're different: More precision is involved. See 1.
Yes, see 1.
To minimize accumulation of floating point errors for multi-term floating point constant expressions.
Of course yes. Can achieving lower precision be ever a goal? It's enough that run-time floating point operations are intrinsically imperfect, no need to add more imprecision from constant expressions.
Problem
Here's the sample code in go: ``` package main import "fmt" func mult32(a, b float32) float32 { return a*b } func mult64(a, b float64) float64 { return a*b } func main() { fmt.Println(3*4.3) // A1, 12.9 fmt.Println(mult32(3, 4.3)) // B1, 12.900001 fmt.Println(mult64(3, 4.3)) // C1, 12.899999999999999 fmt.Println(12.9 - 3*4.3) // A2, 1.8033161362862765e-130 fmt.Println(12.9 - mult32(3, 4.3)) // B2, -9.536743e-07 fmt.Println(12.9 - mult64(3, 4.3)) // C2, 1.7763568394002505e-15 fmt.Println(12.9 - 3*4.3) // A4, 1.8033161362862765e-130 fmt.Println(float32(12.9) - float32(3)*float32(4.3)) // B4, -9.536743e-07 fmt.Println(float64(12.9) - float64(3)*float64(4.3)) // C4, 1.7763568394002505e-15 } ``` Results differences between lines A1, B1 and C1 are understandable. However, starting from A2 to C2 magic comes. Result from neither of B2 nor C2 matches the result from A2 line. The same is true for lines x2 (x = A, B or C) - but the outputs of x2 and x4 are the same. Just to be sure let's print the results in the binary form. ``` fmt.Printf("%b\n", 3*4.3) // A11, 7262054399134925p-49 fmt.Printf("%b\n", mult32(3, 4.3)) // B11, 13526631p-20 fmt.Printf("%b\n", mult64(3, 4.3)) // C11, 7262054399134924p-49 fmt.Printf("%b\n", 12.9 - 3*4.3) // A12, 4503599627370496p-483 fmt.Printf("%b\n", 12.9 - mult32(3, 4.3)) // B12, -8388608p-43 fmt.Printf("%b\n", 12.9 - mult64(3, 4.3)) // C12, 4503599627370496p-101 fmt.Printf("%b\n", 12.9 - 3*4.3) // A14, 4503599627370496p-483 fmt.Printf("%b\n", float32(12.9) - float32(3)*float32(4.3)) // B14, -8388608p-43 fmt.Printf("%b\n", float64(12.9) - float64(3)*float64(4.3)) // C14, 4503599627370496p-101 ``` Some facts from the code above (one in the bin form): - There is difference between line A11 and C11 (last digit - just before the exponent). - Lines A12 and C12 are almost the same (except the exponen!!!), the same can be observed between line A14 and C14. And here the questions come: - How computations of bare (naked :)) numbers are performed? (computations in every Axx line) - Are they performed by compiler/whatever? - If yes, then why are they different? Optimisation? - Are they computed in some system which differs from IEE-754? - If yes, why so? - Is achieving more accurate precision justifiest such approach? Code has been tested on 64bit linux under both "go run" and "go build" (go1.0.3), and also on that site: http://tour.golang.org/