6

这当然是坏的:

(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。

4

2 回答 2

1

准确求和和有效比较是有区别的。你说你想要前者,但看起来你想要后者。底层的 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
于 2013-03-15T15:42:05.413 回答
-1

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)

……应该不错。

于 2013-03-15T09:27:50.777 回答