【问题标题】:Why would Ruby fail equality on 2 floats that appear the same?为什么 Ruby 在两个看起来相同的浮点数上会失败?
【发布时间】:2012-11-22 07:16:11
【问题描述】:

我有一个计算可以生成看起来像 Float 22.23 和文字 22.23 的东西,如下所示:

some_object.total => 22.23
some_object.total.class => Float

22.23 => 22.23
22.23.class => Float

但由于某种原因,以下是错误的:

some_object.total == 22.23 ? true : false

古怪,对吧?

是否使用了某种精确机制,可能通过 some_object.total 调用不完全透明?

【问题讨论】:

标签: ruby-on-rails ruby


【解决方案1】:

浮点数不能精确地表示其范围内的所有十进制数。例如,0.9 并不完全是 0.9,它是一个非常接近 0.9 的数字,在大多数情况下最终会被打印出来。当您进行浮点计算时,这些错误可能会累积,最终您会得到非常接近正确数字但不完全等于它的数字。例如,0.3 * 3 == 0.9 将返回 false。在您将使用的每一种计算机语言中都是如此——这就是二进制浮点数学的工作原理。例如,请参阅this question about Haskell

要测试浮点是否相等,您通常需要测试数字是否在目标的某个微小范围内。所以,例如:

def float_equal(a, b)
  if a + 0.00001 > b and a - 0.00001 < b
    true
  else
    false
  end
end

您还可以使用 Ruby 中的 BigDecimal 类来表示任意十进制数。

如果这是一个测试用例,你可以使用assert_in_delta:

def test_some_object_total_is_calculated_correctly
  assert_in_delta 22.23, some_object.total, 0.01
end

【讨论】:

  • 只是为了表明它不是来自 Chuck 的帖子,这不是 ruby​​ 浮点限制。这是一个浮点限制,期限。它在每一种使用 IEEE 浮点的语言中都以这种方式工作。数值问题是一个重要且有些深奥的主题,您可能会在任何计算机科学课程中的某个时间点进行介绍。
  • 谢谢兄弟。 assert_in_delta 方法为我做到了。我得到:“ 预期但为 ”并且不明白发生了什么!
【解决方案2】:

Float#to_sFloat#inspect 圆形。试试"%.30f" % some_object.total,你会发现不是22.23。

【讨论】:

  • 嘿..."22.229999999999996873611962655559" 就是这样。谢谢。
【解决方案3】:

这里还有其他事情发生。这是来自 1.8.7 irb

irb(main):001:0> class Test
irb(main):002:1> attr_accessor :thing
irb(main):003:1> end
=> nil
irb(main):004:0> t = Test.new
=> #<Test:0x480ab78>
irb(main):005:0> t.thing = 22.5
=> 22.5
irb(main):006:0> t.thing == 22.5
=> true

【讨论】:

  • 总数实际上是其他几个数字的总和和计算,导致不准确。
猜你喜欢
  • 1970-01-01
  • 2017-07-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-03
  • 2016-04-05
相关资源
最近更新 更多