Why do these two float64s have different values?
Asked Answered
R

2

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?

Radiotransparent answered 7/1, 2015 at 12:4 Comment(2)
possible duplicate of Is floating point math broken?Cioffred
@Cioffred Not really. OP is wondering why these two yield different results, not why one of the results is not exactly 9.12.Alexi
A
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.

Alexi answered 7/1, 2015 at 12:24 Comment(0)
R
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

Rossy answered 7/1, 2015 at 12:20 Comment(2)
"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?Radiotransparent
@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.Rossy

© 2022 - 2024 — McMap. All rights reserved.