英文:
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)
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
英文:
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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论