在 RSpec 中比较十进制值的正确方法?

los*_*193 7 ruby rspec ruby-on-rails decimal

我有这个规范来检查预期的数量:

  expect(animal_cost).to eq(0.7771118644067795e5)
Run Code Online (Sandbox Code Playgroud)

我收到一条错误消息:

  expected: 77711.18644067795
        got: 0.7771118644067795e5
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,我得到的数字与我预期的相同,所以我很困惑为什么会失败。如何设置规格?

animal_cost.class是一个 BigDecimal。

nat*_*vda 6

使用 rspec 验证浮点数的最佳方法是使用be_within匹配器。浮点数不能用二进制在数学上完全准确地表示。必然存在一些舍入误差。

在你的情况下,人们会写:

expect(animal_cost).to be_within(0.001).of(0.7771118644067795e5)
Run Code Online (Sandbox Code Playgroud)


Ste*_*fan 4

\n

[animal_cost] 等于0.7771118644067795e5[...] it\'sBigDecimal

\n
\n

这是一个浮点问题。您的BigDecimal值为 0.7771118644067795e5 的精确值为:

\n
77711.18644067795\n
Run Code Online (Sandbox Code Playgroud)\n

77711.18644067795另一方面,浮点数的实际值为:(与大多数语言一样,Ruby 截断浮点数)

\n
77711.186440677949576638638973236083984375\n
Run Code Online (Sandbox Code Playgroud)\n

根据比较的不同,这些值可能会或可能不会被视为“相等”:

\n
d = BigDecimal(\'0.7771118644067795e5\')\nf = 0.7771118644067795e5\n\nd == f      #=> true\nf == d      #=> true\n\nd.eql?(f)   #=> true\nf.eql?(d)   #=> false\n
Run Code Online (Sandbox Code Playgroud)\n

后者返回,false因为这就是Float#eql?工作原理:

\n
\n

eql?(obj) \xe2\x86\x92 true 或 false

\n

true仅当objaFloat与 的值相同时才返回float

\n
\n

为了使测试通过准确的值,您应该使用 aBigDecimal而不是 float:

\n
expect(animal_cost).to eq(BigDecimal(\'0.7771118644067795e5\'))\n
Run Code Online (Sandbox Code Playgroud)\n

就我个人而言,我会避免这样的例子,因为你(显然)将结果复制到期望中。这个值是否正确并不明显。(小数点后 11 位的“成本”对我来说似乎是错误的)尝试更改示例数据以获得可理解的结果,也许还有一个“完整”数字,例如 70,000。

\n