为什么 ghci 说 1.1 + 1.1 + 1.1> 3.3 是真的吗?
我最近正在学习 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
有人知道这是为什么吗?
I've been going through a Haskell tutorial recently and noticed this behaviour when trying some simple Haskell expressions in the interactive ghci
shell:
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
Does anybody know why that is?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
因为
1.1
和3.3
是浮点数。十进制分数(例如 0.1 或 0.3)不能完全用二进制浮点数表示。 .1 表示 1/10。要以二进制表示,其中每个小数位代表 1/2n(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(任意大的整数,Haskell 中的 Integer,而不是固定大小的 Int)作为分子和分母的类型,您可以表示任意精确的有理数,但速度明显慢于浮点数,浮点数是在硬件中实现并针对速度进行优化的。
浮点数是科学和数值计算的一种优化,它以精度换取高速,只要您了解舍入及其对计算的影响,您就可以在短时间内执行大量计算。
Because
1.1
and3.3
are floating point numbers. Decimal fractions, such as .1 or .3, are not exactly representable in a binary floating point number. .1 means 1/10. To represent that in binary, where each fractional digit represents 1/2n (1/2, 1/4, 1/8, etc), you would need an infinite number of digits, 0.000110011... repeating infinitely.This is exactly the same problem as representing, say, 1/3 in base 10. In base 10, you would need an infinite number of digits, .33333... forever, to represent 1/3 exactly. So working in base 10, you usually round, to something like .33. But if you add up three copies of that, you get .99, not 1.
For far more information on the topic, read What Every Computer Scientist Should Know About Floating Point Arithmetic.
For representing rational numbers more precisely in Haskell, you can always use the rational data type,
Ratio
; coupled with bignums (arbitrarily large integers,Integer
in Haskell, as opposed toInt
which are fixed size) as the type for numerator and denominator, you can represent arbitrarily precise rational numbers, but at a significantly slower speed than floating point numbers, which are implemented in hardware and optimized for speed.Floating point numbers are an optimization, for scientific and numerical computation, that trade off precision for high speed, allowing you to perform a very large number of computations in a small time, as long as you are aware of rounding and how it affects your computations.
因为浮点数不准确
(维基百科)
Because floating-point numbers are not accurate
(wikipedia)
您可以使用有理类型来避免 Haskell 中的浮点错误:
当然,您会因为提高的准确性而付出性能损失。
You can avoid floating-point errors in Haskell using rational types:
Of course, you pay a performance penalty for the increased accuracy.
看起来像是典型的浮点错误问题。
请参阅 什么是浮点/舍入误差的简单示例?
Looks like a typical floating point error issue.
See What is a simple example of floating point/rounding error?
它与 IEEE 浮点数的工作方式有关。
1.1 浮点表示为 1.1000000000000001,3.3 表示为 3.2999999999999998。
所以 1.1 + 1.1 + 1.1 实际上是
1.1000000000000001 + 1.1000000000000001 + 1.1000000000000001 = 3.3000000000000003
正如你所看到的,它实际上大于 3.299999999 9999998。
通常的解决方法是要么不评估相等性,要么检查数字是否在目标+/-小epsilon(定义您需要的精度)之内。
例如:如果两者都为真,则总和“等于”3.3(在允许的误差范围内)。
It has to do with the way IEEE floating point numbers work.
1.1 is represented as 1.1000000000000001 in floating point, 3.3 is represented as 3.2999999999999998.
So 1.1 + 1.1 + 1.1 is actually
1.1000000000000001 + 1.1000000000000001 + 1.1000000000000001 = 3.3000000000000003
Which, as you can see is actually larger than 3.2999999999999998.
The usual workaround is to either not evaluate equality, or to check if a number is within the target +/- a small epsilon (which defines the accuracy you need).
Ex: if both are true, then the sum is "equal" to 3.3 (within the allowed error).
很少有浮点数可以使用 IEEE 754 表示法精确表达,因此它们总是会有点偏差。
Few floats can be expressed exactly using IEEE 754 representation, so they will always be a little off.
一般来说,您不应该比较浮点数是否相等(出于上述原因)。我能想到的唯一原因是如果你想说“这个值改变了吗?”例如,“if (newscore /= oldscore)”则采取一些操作。只要您不比较两个单独计算的结果来检查它们是否恰好相等(因为即使从数学上讲,如果它们相等,它们也可能会舍入),就可以了。
In general, you shouldn't be comparing floats for equality (for the reasons outlined above). The only reason I can think of is if you want to say "has this value changed?" For example, "if (newscore /= oldscore)" then take some action. That is okay as long as you are not comparing the result of two separate computations to check whether they happen to be equal (because then even mathematically if they are, they might round otherwise).