我最近经历了一个Haskell教程,并在交互式ghci
shell中尝试一些简单的Haskell表达式时注意到了这种行为:
Prelude> 1.1 + 1.1 == 2.2
True
Prelude> 1.1 + 1.1 + 1.1 == 3.3
False
Prelude> 1.1 + 1.1 + 1.1 > 3.3
True
Prelude> 1.1 + 1.1 + 1.1
3.3000000000000003
有人知道那是为什么吗?
答案 0 :(得分:36)
因为1.1
和3.3
是floating point numbers。十进制分数(例如.1或.3)在二进制浮点数中不能精确表示。 .1表示1/10。为了用二进制表示,每个小数位代表1/2 n (1 / 2,1 / 4,1 / 8等),你需要无限数量的数字,0.000110011 ..无限重复。
这与在基数10中表示1/3的问题完全相同。在基数10中,您将需要无限数量的数字,.33333 ......永远,以准确地表示1/3。所以在10号基地工作,你通常会变成类似.33的东西。但是,如果你加上三个副本,你得到.99,而不是1。
有关该主题的更多信息,请阅读What Every Computer Scientist Should Know About Floating Point Arithmetic。
为了在Haskell中更精确地表示有理数,你总是可以使用有理数据类型Ratio
;加上bignums(任意大整数,Haskell中的Integer
,而不是固定大小的Int
)作为分子和分母的类型,你可以表示任意精确的有理数,但速度要慢得多速度比浮点数,它是在硬件中实现的,并针对速度进行了优化。
浮点数是一种优化,用于科学计算和数值计算,它可以在高速下折衷精度,只要你知道舍入及其如何,就可以在很短的时间内执行大量的计算。影响你的计算。
答案 1 :(得分:13)
因为浮点数不准确 (wikipedia)
答案 2 :(得分:13)
您可以使用有理类型来避免Haskell中的浮点错误:
Prelude Data.Ratio> let a = (11 % 10) + (11 % 10) + (11 % 10)
Prelude Data.Ratio> a > (33 % 10)
False
Prelude Data.Ratio> fromRational a
3.3
当然,您为提高准确性而支付了性能损失。
答案 3 :(得分:6)
看起来像是一个典型的浮点错误问题。
请参阅What is a simple example of floating point/rounding error?
答案 4 :(得分:6)
它与IEEE浮点数的工作方式有关。
1.1在浮点表示为1.1000000000000001,3.3表示为3.2999999999999998。
所以1.1 + 1.1 + 1.1实际上是
1.1000000000000001 + 1.1000000000000001 + 1.1000000000000001 = 3.3000000000000003
其中,正如您所看到的,实际上大于3.2999999999999998。
通常的解决方法是要么不评估相等性,要么检查一个数字是否在目标范围内+/-一个小epsilon(它定义了你需要的准确度)。
例如:如果两者都为真,那么总和“等于”3.3(在允许的误差范围内)。
1.1 + 1.1 + 1.1 < 3.3 + 1e9
1.1 + 1.1 + 1.1 > 3.3 - 1e9
答案 5 :(得分:5)
很少有浮点数可以使用IEEE 754表示法精确表达,因此它们总是稍微偏离。
答案 6 :(得分:1)
通常,您不应该将浮点数进行比较(出于上述原因)。我能想到的唯一原因是,如果你想说“这个价值有变化吗?”例如,“if(newscore / = oldscore)”然后采取一些行动。只要你没有比较两个单独计算的结果来检查它们是否恰好相等(那么即使它们是数学上,否则它们可能会反过来),这是可以的。