英文:
Why does 0.1 + 0.2 get 0.3 in Google Go?
问题
只要使用浮点数,0.1就无法在内存中精确表示,所以我们知道这个值通常会变成0.10000000000000004。
但是当使用Go语言将0.1和0.2相加时,结果是0.3。
fmt.Println(0.1 + 0.2)
// 输出:0.3
为什么结果是0.3而不是0.30000000000000004?
英文:
As long as floating point is used, 0.1 can not be represented exactly in memory, so we know that this value usually comes out to 0.10000000000000004.
But when using go to add 0.1 and 0.2.
I'm getting 0.3.
fmt.Println(0.1 + 0.2)
// Output : 0.3
Why is 0.3 coming out instead of 0.30000000000000004 ?
答案1
得分: 10
这是因为当你打印它时(例如使用fmt
包),打印函数已经对小数位数进行了四舍五入。
看看这个例子:
const ca, cb = 0.1, 0.2
fmt.Println(ca + cb)
fmt.Printf("%.20f\n", ca+cb)
var a, b float64 = 0.1, 0.2
fmt.Println(a + b)
fmt.Printf("%.20f\n", a+b)
输出结果(在Go Playground上尝试):
0.3
0.29999999999999998890
0.30000000000000004
0.30000000000000004441
首先,我们使用了常量,因为这与使用float64
类型的(非常量)值是不同的。数值常量表示精确的任意精度值,不会溢出。
但是,当打印ca+cb
的结果时,常量值必须转换为非常量、有类型的值,以便传递给fmt.Println()
。这个值将是float64
类型的,它不能精确表示0.3
。但是fmt.Println()
会将其四舍五入到大约16位小数位,即0.3
。但是当我们明确指定要显示20位小数时,我们会发现它不是精确的。请注意,只有0.3
会被转换为float64
,因为常量算术0.1+0.2
将由编译器(在编译时)计算。
接下来,我们从float64
类型的变量开始,毫不奇怪,输出结果并不完全是0.3
,但这次即使使用默认的四舍五入,我们得到的结果也与0.3
不同。原因是在第一种情况(常量)中,转换的是0.3
,但这次0.1
和0.2
都被转换为float64
,它们都不是精确的,将它们相加得到的数字与0.3
的距离更大,足够大以至于在fmt
包的默认四舍五入下产生一个“视觉上的差异”。
查看类似/相关的问题+答案以了解更多关于这个主题的信息:
https://stackoverflow.com/questions/26120311/why-does-adding-0-1-multiple-times-remain-lossless
https://stackoverflow.com/questions/39544571/golang-round-to-nearest-0-05/39544897#39544897
https://stackoverflow.com/questions/33206059/go-converting-float64-to-int-with-multiplier
英文:
It is because when you print it (e.g. with the fmt
package), the printing function already rounds to a certain amount of fraction digits.
See this example:
const ca, cb = 0.1, 0.2
fmt.Println(ca + cb)
fmt.Printf("%.20f\n", ca+cb)
var a, b float64 = 0.1, 0.2
fmt.Println(a + b)
fmt.Printf("%.20f\n", a+b)
Output (try it on the Go Playground):
0.3
0.29999999999999998890
0.30000000000000004
0.30000000000000004441
First we used constants because that's different than using (non-constant) values of type float64
. Numeric constants represent exact values of arbitrary precision and do not overflow.
But when printing the result of ca+cb
, the constant value have to be converted to a non-constant, typed value to be able to be passed to fmt.Println()
. This value will be of type float64
, which cannot represent 0.3
exactly. But fmt.Println()
will round it to like ~16 fraction digits, which will be 0.3
. But when we explicitly state we want it displayed with 20 digits, we'll see it's not exact. Note that only 0.3
will be converted to float64
, because the constant arithmetic 0.1+0.2
will be evaluated by the compiler (at compile time).
Next we started with variables of type float64
, and to no surprise, output wasn't 0.3
exactly, but this time even with the default rounding we got a result different from 0.3
. The reason for this is because in the first case (constants) it was 0.3
that was converted, but this time both 0.1
and 0.2
were converted to float64
, none of which is exact, and adding them resulted in a number having bigger distance from 0.3
, big enough to make a "visual appearance" with the default rounding of the fmt
package.
Check out similar / relevant questions+answers to know more about the topic:
https://stackoverflow.com/questions/26120311/why-does-adding-0-1-multiple-times-remain-lossless
https://stackoverflow.com/questions/39544571/golang-round-to-nearest-0-05/39544897#39544897
https://stackoverflow.com/questions/33206059/go-converting-float64-to-int-with-multiplier
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论