为什么这两个 float64 类型的值不同?

huangapple go评论77阅读模式
英文:

Why do these two float64s have different values?

问题

考虑以下两种情况:

fmt.Println(912 * 0.01)
fmt.Println(float64(912) * 0.01)

第二行打印的结果是9.120000000000001,这是可以理解的,我知道为什么会这样

然而,为什么第一行打印的结果是9.12,没有末尾的...01呢?Go语言是否在编译时将这两个未命名常量相乘,并简单地用9.12替换它们?

英文:

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?

答案1

得分: 15

根据规范

> 常量表达式总是被精确计算;中间值和常量本身可能需要比语言中任何预声明类型支持的精度更高。

由于

912 * 0.01

是一个常量表达式,它被精确计算。因此,写fmt.Println(912 * 0.01)与写fmt.Println(9.12)具有相同的效果。当你将912固定为float64时,浮点数乘法的另一个操作数也会被隐式固定为float64。因此,表达式float64(912) * 0.01的行为类似于float64(912) * float64(0.01)。0.01在float64中无法精确表示,因此精度丢失发生在不同的地方,而不是在你的第一个示例中fmt.Println()的参数中出现的表达式float64(912 * 0.01),这解释了不同的结果。

英文:

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.

答案2

得分: 6

不同输出的原因是,在第一种情况下,912 * 0.01 是两个未指定类型的常量值的乘法,其精度是任意的,只有在将值传递给 Println() 时,结果才会转换为 float64。(有关详细信息,请参阅语言规范中的 常量表达式 部分。)

而在第二种情况下,float64(912) * 0.01 首先将 912 转换为 float64,然后将未指定类型的常量 0.01 转换为 float64,这两个具有 float64 类型的值相乘,不是任意精度,并且不会给出精确结果。

注意:

在第一种情况下,当传递给 Println() 时,结果将被转换为 float64

fmt.Printf("%T %v\n", 912 * 0.01, 912 * 0.01)

输出:

float64 9.12

在 Go Playground 上测试

英文:

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

huangapple
  • 本文由 发表于 2015年1月7日 20:04:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/27819137.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定