我最近一直在阅读 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
有人知道这是为什么吗?
我最近一直在阅读 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
有人知道这是为什么吗?
因为1.1
和3.3
是浮点数。十进制小数,例如 .1 或 .3,在二进制浮点数中不能精确表示。.1 表示 1/10。要以二进制表示,其中每个小数位代表 1/2 n(1/2、1/4、1/8 等),您将需要无限数量的数字,0.000110011 ......无限重复。
这与表示以 10 为底的 1/3 完全相同的问题。在以 10 为底的情况下,您将需要无限数量的数字,0.33333... 永远,才能准确地表示 1/3。因此,在以 10 为底的情况下,您通常会四舍五入到 0.33 左右。但是如果你把它的三个副本加起来,你得到 0.99,而不是 1。
有关该主题的更多信息,请阅读每个计算机科学家应该了解的关于浮点运算的知识。
为了在 Haskell 中更精确地表示有理数,您始终可以使用有理数据类型Ratio
;再加上 bignums(Integer
在 Haskell 中是任意大的整数,而不是Int
固定大小)作为分子和分母的类型,您可以表示任意精确的有理数,但速度比浮点数慢得多,后者在硬件并针对速度进行了优化。
浮点数是一种优化,用于科学和数值计算,以牺牲精度换取高速,允许您在短时间内执行大量计算,只要您了解舍入以及它如何影响您的计算.
因为浮点数不准确 (维基百科)
您可以使用有理类型避免 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
当然,您会为提高的准确性付出性能损失。
看起来像一个典型的浮点错误问题。
它与 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
很少有浮点数可以使用 IEEE 754 表示法精确表达,因此它们总是会有一点偏差。
一般来说,您不应该比较浮点数是否相等(出于上述原因)。我能想到的唯一原因是如果你想说“这个值改变了吗?” 例如,“if (newscore /= oldscore)”然后采取一些行动。只要您不比较两个单独计算的结果以检查它们是否恰好相等,那就可以了(因为即使在数学上它们相等,它们也可能会舍入)。