Reputation: 3363
In the simple calculations below, c
and d
end up with different values (they're off by a single bit). Why is that?
a := 4000.0
b := 1e-9
c := a / b
d := 4000.0 / 1e-9
Upvotes: 3
Views: 240
Reputation: 24898
a
is assigned the next-best value for 4000.0
represented as a float64
, which is 0x40AF400000000000
and corresponds to exactly 4000.0
.
b
is the next-best value for 1e-9
, represented as a float64
, which is 0x3E112E0BE826D695
and corresponds to 1.00000000000000006228159145778E-9
. You lost precision here.
So before computing c
you already have a slight imprecision. Then when c
is actually calculated you lose some more precision to rounding.
In the case of d
, there is only one "next-besting" going on; when representing the compile-time calculated full-precision value of 4000.0 / 1e-9
as a float64
.
An aside about variables: In C (and presumably C++), the default is to assume a variable is non-volatile i.e. not shared and hence optimizations often follow constant-expressions deeply, replacing everything with computed values. Go makes no assumptions about the scope of variables and hence no guarantees about replacing constant expressions. This may change in the future but today, not much is done in that department so it's quite possible that c
is actually calculated at runtime, not at compile-time as someone used to C might think.
Edit: I have taken the program provided by @topskip:
package main
import "fmt"
func main() {
a := 4000.0
b := 1e-9
c := a / b
d := 4000.0 / 1e-9
fmt.Println(a)
fmt.Println(b)
fmt.Println(c)
fmt.Println(d)
}
And this is part of the assembly generated by go tool 6g -S
:
0x0021 00033 (meh.go:6) MOVSD $f64.40af400000000000+0(SB),X2
0x002a 00042 (meh.go:7) MOVSD $f64.3e112e0be826d695+0(SB),X3
0x0033 00051 (meh.go:8) MOVAPD X2,X0
0x0037 00055 (meh.go:8) MOVSD X3,"".b+64(SP)
0x003d 00061 (meh.go:8) DIVSD X3,X0
As you can see; c
is calculated at runtime based on the two constants I describe.
Upvotes: 6