为什么在添加0.1和0.2的情况下浮动和双重不同?

时间:2017-02-17 07:10:47

标签: haskell

有人可以解释原因吗

(0.1::Float) + (0.2::Float) == (0.3::Float)                                                                                                      

(0.1::Double) + (0.2::Double) /= (0.3::Double)

据我所知,Double应该更精确。有什么关于Float我应该知道的吗?

2 个答案:

答案 0 :(得分:13)

首先要注意的是,当你输入0.1::Double并且ghci打印0.1时,它只是一种“错觉:”

Prelude Data.Ratio> 0.1::Double
0.1

为什么这是幻觉?因为数字0.1实际上不能精确表示为浮点数!对于FloatDouble都是如此。观察:

Prelude Data.Ratio> toRational (0.1::Float)
13421773 % 134217728
Prelude Data.Ratio> toRational (0.1::Double)
3602879701896397 % 36028797018963968

所以,实际上,这些数字确实“接近”实际实数0.1,但两者都不是0.1。他们有多近?我们来看看:

Prelude Data.Ratio> toRational (0.1::Float) - (1%10)
1 % 671088640
Prelude Data.Ratio> toRational (0.1::Double) - (1%10)
1 % 180143985094819840

如您所见,Double确实比Float精确得多; 0.1作为Double的表示与实际的实数0.1之间的差异要小得多。但两者都不准确。

因此,Double添加确实更加精确,应优先于Float版本。你看到的令人困惑的平等只不过是四舍五入的奇怪效果。 ==的结果应该在浮点域中受信任。实际上,有许多浮点数x使得x == x + 1成立。这是一个例子:

Prelude> let x = -2.1474836e9::Float
Prelude> x == x + 1
True

关于浮点表示的一个很好的阅读是经典What Every Computer Scientist Should Know about Floating-Point Arithmetic,它解释了浮点运算的许多古怪方面。

另请注意,此行为并非Haskell独有。任何使用IEEE754 Floating-point arithmetic的语言都会以这种方式运行,这是现代微处理器实现的标准。

答案 1 :(得分:8)

Double确实更准确。是的,关于浮点表示,你应该知道一些事情:你必须非常小心你如何使用它们! ==通常不太可能实际有用。您通常希望使用更专业的函数来比较浮点表示,或者至少检查表示是否在某个范围内,而不是根据内置的近似值是否具有某个值。