两个非常接近的浮点数不相等

时间:2017-10-15 20:21:52

标签: ruby floating-point

在IRB:

0.9999999999999998 == 1.0
# => false
0.99999999999999998 == 1.0
# => true

同时只显示inspect值:

0.9999999999999998
# => 0.9999999999999998
0.99999999999999998
# => 1.0

在我的情况下,我的程序创建的值不幸是第一个,因此我在编写测试用例时遇到问题,该测试用例将值验证为等于1。

我决定使用以下选项:

  1. 在应用程序代码中添加round次调用,但应用程序已经运行,我无法测试
  2. 在测试代码中添加round次调用
  3. ???
  4. 您的推荐方法是什么?有没有办法我可以配置我的程序在十进制之后用十五个九等于1.0来处理0?感觉有点令人沮丧,因为小数点后的十六个9似乎是截止的 - 我只有一个短线。

1 个答案:

答案 0 :(得分:3)

阅读本文,了解如何比较小的浮点差异:
http://c-faq.com/fp/fpequal.html

我将他们提出的解决方案转换为Ruby:

class Float
  MY_EPSILON   = 0.000000000000001
  MY_TOLERANCE = 0.000000000000001

  def eq_epsilon?(flt)
    a = self
    b = flt

    (a - b).abs <= MY_EPSILON * a.abs
  end

  def self.reldif(a, b)
    c = a.abs
    d = b.abs
    d = [c,d].max

    d == 0.0 ? 0.0 : (a - b).abs / d
  end

  def eq_reldif?(flt)
    Float.reldif(self, flt) <= MY_TOLERANCE
  end
end

因此我们可以运行一些测试代码:

f1 = 0.99999999999999998
f2 = 0.9999999999999998
f3 = 0.999999999999998

[f1, f2, f3].each { |f|
  p f.eq_epsilon?(1.0)
  p 1.0.eq_epsilon?(f)
}

puts "--------------"

[f1, f2, f3].each { |f|
  p f.eq_reldif?(1.0)
  p 1.0.eq_reldif?(f)
}

输出:

true
true
true
true
false
false
--------------
true
true
true
true
false
false

然而,可能需要进行更多测试以确保其满足您的所有要求。