Consider these two cases:
fmt.Println(912 * 0.01)
fmt.Println(float64(912) * 0.01)
(Go Playground link)
The second one prints 9.120000000000001, which is actually fine, I understand why that is happening.
However, why does the first line print 9.12, without the …01 at the end? Does Go multiply the two untyped constants and simply replace them with a 9.12 literal when compiling?
As per spec:
Constant expressions are always evaluated exactly; intermediate values and the constants themselves may require precision significantly larger than supported by any predeclared type in the language.
Since
912 * 0.01
is a constant expression, it is evaluated exactly. Thus, writing fmt.Println(912 * 0.01)
has the same effect as writing fmt.Println(9.12)
. When you pin 912
to float64
, the other operand of the floating-point multiplication is implicitly pinned to float64
, too. Thus, the expression float64(912) * 0.01
behaves like float64(912) * float64(0.01)
. 0.01 is not exactly representable in a float64
, thus precision is lost in a different place than in the expression float64(912 * 0.01)
which arises in the argument of fmt.Println()
in your first example, explaining the different results.
The reason for the different output is that in the first case 912 * 0.01
is the multiplication of 2 untyped constant values which is of arbitrary precision, and only the result is converted to float64
when the value is passed to Println()
. (See Constant expressions section of the Language specification for details.)
In the second case float64(912) * 0.01
first 912
is converted to float64
, then the untyped constant 0.01
is converted to float64
and these two values having float64
are multiplied which is not an arbitrary precision, and will not give exact result.
Note:
In the first case the result will be converted to float64
when passed to the Println()
:
fmt.Printf("%T %v\n", 912 * 0.01, 912 * 0.01)
Output:
float64 9.12
Test it on Go Playground