9

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?

Community
  • 1
  • 1
Attila O.
  • 15,659
  • 11
  • 54
  • 84
  • possible duplicate of [Is floating point math broken?](http://stackoverflow.com/questions/588004/is-floating-point-math-broken) – asawyer Jan 07 '15 at 20:39
  • 1
    @asawyer Not really. OP is wondering why these two yield different results, not why one of the results is not exactly 9.12. – fuz Jan 08 '15 at 11:26

2 Answers2

15

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.

fuz
  • 88,405
  • 25
  • 200
  • 352
6

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

icza
  • 389,944
  • 63
  • 907
  • 827
  • "In the first case the result will be converted to `float64` when passed to the `Println()`" — you mean the result of multiplying two untyped constants, right? – Attila O. Jan 07 '15 at 12:25
  • @AttilaO. Yes, the result of the multiplication. When it is passed to the `Println()` function, it has to be converted to a typed value. Also edited to add code to show the type of the result. – icza Jan 07 '15 at 12:26