平等比较很少能在 FP 值上取得成功
简短的回答是Float#to_d
在 1.9 中更准确,并且正确地未通过 1.8.7 中不应成功的相等测试。
长答案涉及浮点编程的基本规则:从不进行相等比较。相反,模糊比较如if (abs(x-y) < epsilon)
推荐,或者编写代码来完全避免相等比较的需要。
Although there are in theory about 232 single-precision numbers and 264 double-precision numbers that could be exactly compared, there are an infinite number that cannot be so compared. (Note: it is safe to do equality comparisons on FP values that happen to be integral. So, contrary to much advice, they are actually perfectly safe for loop indices and subscripts.)
更糟糕的是,我们编写小数的方式使得与任何特定常量的比较不太可能成功。
那是因为分数是二进制的,即 1/2 + 1/4 + 1/8 ...但我们的常数是十进制的。因此,例如,考虑范围内的货币金额$1.00, $1.01, $1.02 .. $1.99.
此范围内有 100 个值,但其中只有 4 个具有精确的 FP 表示:1.00, 1.25, 1.50, and 1.75.
那么,回到你的问题。你的结果是0.495
没有精确的表示,输入常数也没有0.1.
首先计算两个不同大小的 FP 数的减法。为了完成减法,较小的数字将被非规范化,因此它将丢失两个或三个低位。结果,计算将导致比 0.495 稍大的数字,因为没有从 10 中减去整个 0.1。您的常数实际上(在内部)略小于 0.495。这就是比较失败的原因。
Ruby 1.8 一定是意外或故意丢失了一些低位,并有效地引入了舍入步骤,最终对您的测试有所帮助。
请记住:经验法则是您必须在此类舍入中显式编程以进行浮点比较。
Notes. To answer the question from the comments about simple decimal fraction constants not having exact representations: They don't have exact finite forms because they repeat in binary. Every machine fraction is a rational number of the form x/2n. Now, the constants are decimal and every decimal constant is a rational number of the form x/(2n * 5m). The 5m numbers are odd, so there isn't a 2n factor for any of them. Only when m == 0 is there a finite representation in both the binary and decimal expansion of the fraction. So, 1.25 is exact because it's 5/(22*50) but 0.1 is not because it's 1/(20*51). There is simply no way to express 0.1 as a finite sum of x/2n components.