9

我最近一直在阅读 Haskell 教程,并在交互式ghcishell 中尝试一些简单的 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

有人知道这是为什么吗?

4

7 回答 7

37

因为1.13.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固定大小)作为分子和分母的类型,您可以表示任意精确的有理数,但速度比浮点数慢得多,后者在硬件并针对速度进行了优化。

浮点数是一种优化,用于科学和数值计算,以牺牲精度换取高速,允许您在短时间内执行大量计算,只要您了解舍入以及它如何影响您的计算.

于 2010-01-10T21:22:55.753 回答
14

因为浮点数不准确 (维基百科)

于 2010-01-10T21:17:30.117 回答
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

当然,您会为提高的准确性付出性能损失。

于 2010-01-10T21:34:16.833 回答
6

看起来像一个典型的浮点错误问题。

请参阅什么是浮点/舍入错误的简单示例?

于 2010-01-10T21:16:58.957 回答
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
于 2010-01-10T21:23:49.557 回答
5

很少有浮点数可以使用 IEEE 754 表示法精确表达,因此它们总是会有一点偏差。

于 2010-01-10T21:17:12.163 回答
1

一般来说,您不应该比较浮点数是否相等(出于上述原因)。我能想到的唯一原因是如果你想说“这个值改变了吗?” 例如,“if (newscore /= oldscore)”然后采取一些行动。只要您不比较两个单独计算的结果以检查它们是否恰好相等,那就可以了(因为即使在数学上它们相等,它们也可能会舍入)。

于 2010-06-17T00:58:51.583 回答