【发布时间】:2014-04-15 18:07:20
【问题描述】:
我写了一个程序来演示 Go 中的浮点错误:
func main() {
a := float64(0.2)
a += 0.1
a -= 0.3
var i int
for i = 0; a < 1.0; i++ {
a += a
}
fmt.Printf("After %d iterations, a = %e\n", i, a)
}
打印出来:
After 54 iterations, a = 1.000000e+00
这与用 C 编写的同一程序的行为相匹配(使用 double 类型)
但是,如果改用float32,程序就会陷入无限循环!如果您修改 C 程序以使用 float 而不是 double,它会打印
After 27 iterations, a = 1.600000e+00
为什么Go程序在使用float32时输出与C程序不同?
【问题讨论】:
-
我没有看到问题... 0.2 + 0.1 = 0.3, 0.3 - 0.3 = 0.0, 循环 0.0 + 0.0 永远不会超过 1.0 我很困惑的是你是怎么得到的它用float64跳出循环?
-
浮点数并不完全准确。特别是数字 0.1 和 0.3 不能准确表示。这会导致
a在进入循环之前有一个非零(尽管非常小)的值。维基百科有解释。 en.wikipedia.org/wiki/Guard_digit -
我开始玩这个操场play.golang.org/p/Im6OFfTFPY,我有点明白你的意思,但它看起来在Go中float32s 是精确表示,而float64s不是
-
如果你用
go tool 6g -S main.go检查代码的ASM你会看到原因。 float32 的计算如下:2.00000002980232230e-01 + 1.00000001490116120e-01 - 3.00000011920928950e-01 这是一个负值,永远不会总和为 1。为什么 Go 这样做,我不知道。 -
在另一个游乐场 (play.golang.org/p/FZxCQTS9yG) 玩了一会儿,发现当您将 float64 打印到小数点后 20 位时,您会得到比
0.30...04更多的数字,您会得到 @ 987654335@,其余的被切断。我猜想使用 float32 会截断更多,并舍入到偶数0.3。这可以解释算术,但现在它只是一个理论。
标签: go floating-point precision