有人可以解释原因吗
(0.1::Float) + (0.2::Float) == (0.3::Float)
而
(0.1::Double) + (0.2::Double) /= (0.3::Double)
据我所知,Double应该更精确。有什么关于Float我应该知道的吗?
答案 0 :(得分:13)
首先要注意的是,当你输入0.1::Double
并且ghci打印0.1
时,它只是一种“错觉:”
Prelude Data.Ratio> 0.1::Double
0.1
为什么这是幻觉?因为数字0.1
实际上不能精确表示为浮点数!对于Float
和Double
都是如此。观察:
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
确实更准确。是的,关于浮点表示,你应该知道一些事情:你必须非常小心你如何使用它们! ==
通常不太可能实际有用。您通常希望使用更专业的函数来比较浮点表示,或者至少检查表示是否在某个范围内,而不是根据内置的近似值是否具有某个值。