这当然是坏的:
(0.1 + 0.1 + 0.1) => 0.30000000000000004
(0.1 + 0.1 + 0.1) == 0.3 # false
我不需要一个完美的总和,只需说两个浮点数相同即可。我能想到的最好的办法是将等式的两边相乘并四舍五入。这是最好的方法吗?
((0.1 + 0.1 + 0.1) * 1000).round == (0.3 * 1000).round
更新:我坚持使用 Ruby v1.8.7。
这当然是坏的:
(0.1 + 0.1 + 0.1) => 0.30000000000000004
(0.1 + 0.1 + 0.1) == 0.3 # false
我不需要一个完美的总和,只需说两个浮点数相同即可。我能想到的最好的办法是将等式的两边相乘并四舍五入。这是最好的方法吗?
((0.1 + 0.1 + 0.1) * 1000).round == (0.3 * 1000).round
更新:我坚持使用 Ruby v1.8.7。
准确求和和有效比较是有区别的。你说你想要前者,但看起来你想要后者。底层的 Ruby 浮点算法是 IEEE 的,并且具有最小化累积误差的合理语义,但在使用不能准确表示所有值的表示时总会出现这种情况。为了准确地建模误差,FP 加法不应该产生一个精确的值,它应该产生一个区间,进一步的加法将在区间上进行。
在实践中,许多应用程序不需要详细说明错误,它们只需要进行计算并注意比较不准确,输出十进制表示应该四舍五入。
这是 Float 的一个简单扩展,可以帮助您进行比较。它或类似的东西应该在标准库中,但不是。
class Float
def near_enough?(other, epsilon = 1e-6)
(self - other.to_f).abs < epsilon.to_f
end
end
pry(main)> (0.1 + 0.1 + 0.1).near_enough?(0.3)
=> true
pry(main)> (0.1 + 0.1 + 0.1).near_enough?(0.3, 1e-17)
=> false
pry(main)> ( [0.1] * (10**6) ).reduce(:+).near_enough?(10**5, 1e-5)
=> true
pry(main)> ( [0.1] * (10**6) ).reduce(:+).near_enough?(10**5)
=> false
在一般情况下,选择合适的epsilon
可能会很棘手。你应该阅读每个计算机科学家应该知道的关于浮点运算的知识。我发现 Bruce Dawson 的浮点技巧博客很棒,这是他关于比较浮点数的章节
如果你真的关心准确性,你可以使用精确的表示来做你的算术。Ruby 提供了一个Rational类(甚至早在 1.8 中),它让您可以对分数进行精确的算术运算。
pry(main)> r=Rational(1,10)
=> (1/10)
pry(main)> (r + r + r) == Rational(3,10)
=> true
pry(main)> (r + r + r) == 0.3
=> true
pry(main)> r.to_f
=> 0.1
pry(main)> (r + r + r).to_f
=> 0.3
round 方法支持指定要四舍五入的小数位数:http ://www.ruby-doc.org/core-1.9.3/Float.html#method-i-round
所以
(0.1 + 0.1 + 0.1).round(1) == (0.3).round(1)
……应该不错。