lost9123193
lost9123193

Reputation: 11040

Proper way to compare decimal values in RSpec?

I have this spec that checks for the expected number:

  expect(animal_cost).to eq(0.7771118644067795e5)

I get an error that says:

  expected: 77711.18644067795
        got: 0.7771118644067795e5

As you can see, the number I got is the same as the one I expected so I'm confused why this is failing. How can I set the spec?

animal_cost.class is a BigDecimal.

Upvotes: 7

Views: 2861

Answers (2)

nathanvda
nathanvda

Reputation: 50057

The best way to verify floats using rspec is to use the be_within matcher. Floats cannot be represented completely mathematically accurate in binary. There are bound to be some rounding errors.

In your case one would write:

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

Upvotes: 6

Stefan
Stefan

Reputation: 114238

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

It's a floating point issue. Your BigDecimal with a value of 0.7771118644067795e5 has an exact value of:

77711.18644067795

The float 77711.18644067795 on the other hand has an actual value of: (like most languages, Ruby truncates floats)

77711.186440677949576638638973236083984375

Depending on the comparison, these values might or might not be treated as "equal":

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

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

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

The latter returns false because that's how Float#eql? works:

eql?(obj) → true or false

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

In order to make the test pass with the exact value, you should use a BigDecimal instead of a float:

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

Personally, I'd avoid such examples because you're (apparently) copying the result into the expectation. It's not at all obvious whether this value is correct or not. (a "cost" with 11 decimal places seems wrong to me) Try to change your example data to get a comprehensible result and maybe also a "whole" number like e.g. 70,000.

Upvotes: 4

Related Questions