比较 RSpec 中小数值的正确方法?

Proper way to compare decimal values in RSpec?

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

  expect(animal_cost).to eq(0.7771118644067795e5)

我收到一条错误消息:

  expected: 77711.18644067795
        got: 0.7771118644067795e5

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

animal_cost.class 是一个 BigDecimal。

[animal_cost] equals 0.7771118644067795e5 [...] it's a BigDecimal

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

77711.18644067795

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

77711.186440677949576638638973236083984375

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

d = BigDecimal('0.7771118644067795e5')
f = 0.7771118644067795e5

d == f      #=> true
f == d      #=> true

d.eql?(f)   #=> true
f.eql?(d)   #=> false

后者 returns false 因为这就是 Float#eql? 的工作方式:

eql?(obj) → true or false

Returns true only if obj is a Float with the same value as float.

为了使测试通过准确的值,您应该使用 BigDecimal 而不是浮点数:

expect(animal_cost).to eq(BigDecimal('0.7771118644067795e5'))

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

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

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

expect(animal_cost).to be_within(0.001).of(0.7771118644067795e5)