Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

rspec big decimal matcher

-:total_cost_with_tax => #<BigDecimal:7fda9d17aaf0,'0.105225E4',18(45)>,
-:total_cost_without_tax => #<BigDecimal:7fda9d17b450,'0.972E3',9(36)>,
-:total_last_installment_amount => #<BigDecimal:7fda9d17b978,'0.8011E2',18(45)>,
-:total_monthly_installment_amount => #<BigDecimal:7fda9d17abb8,'0.8011E2',18(45)>,
-:total_tax => #<BigDecimal:7fda9d17b068,'0.8025E2',18(45)>,

+:total_cost_with_tax => #<BigDecimal:7fda9d0184c8,'0.105225E4',18(36)>,
+:total_cost_without_tax => #<BigDecimal:7fda91ff2b48,'0.972E3',9(27)>,
+:total_last_installment_amount => #<BigDecimal:7fda91fee548,'0.8011E2',18(36)>,
+:total_monthly_installment_amount => #<BigDecimal:7fda91fe72c0,'0.8011E2',18(36)>,
+:total_tax => #<BigDecimal:7fda9d00a2b0,'0.8025E2',18(36)>,

So, these are littered throughout some of my tests... rspec 2, rails 3. I'm comparing hashes using .should eq() to compare. I can't seem to get the incantation right. Seems like it's a precision thing, which seems silly.

like image 877
Dudo Avatar asked Oct 09 '14 00:10

Dudo


1 Answers

RSpec 3 has BigDecimal eq:

x.should eq(y)

expect(x).to eq(y)

If you're comparing a BigDecimal to a Float, be aware the precision can affect the comparison.

You can use this:

x.should be_within(delta).of(y)

expect x.to be_within(delta).of(y)

If you're comparing two BigDecimal numbers that have different precisions, be aware that these numbers can show different results from inspect and also from hash depending on your platform and what version of Ruby BigDecimal you're running.

For example, this can happen:

BigDecimal.new("2").hash == BigDecimal.new("2.0").hash
=> false

Your output shows that your BigDecimal string representations are slightly different.

Here's what your strings mean:

  • Part 1 is the object address.
  • Part 2 is the number value represented as a string.
  • Part 3 is the number of significant digits then the maximum number of significant digits.

Your output shows that your strings have different object addresses, identical number values (i.e. part 2), identical significant digits (the first number in part 3), but different numbers of maximum significant digits.

For your question, using RSpec 2 and comparing BigDecimal hashes, you'll solve your issue by using the rspec be_within matcher.

Note that Ruby BigDecimal numbers and Float numbers are both floating point numbers:

  • A BigDecimal is a arbitrary-precision decimal floating point number.

  • A Float is the native architecture's double-precision binary floating point number.

You can see the BigDecimal decimal floating-point representation by doing this:

require 'bigdecimal'
x=BigDecimal.new(100)
=> #<BigDecimal:7f8e62038570,'0.1E3',9(27)>

The significand part is '0.1' and the exponent part is 'E3'.

Note that this is for the typical Ruby MRI/KRI VM. Implementations may be different on other Ruby VMs, such as JRuby, because Java has its own bignum code.

When you compare two different types of floating-point numbers, such as BigDecimal and Float, you can get results that may seem counter-intuitive because the types use different bases (decimal vs. binary), different precisions, and different Ruby classes.

Example:

BigDecimal.new("1.111111111111111") === 1.111111111111111
=> true
BigDecimal.new("1.1111111111111111") === 1.1111111111111111
=> false
like image 61
joelparkerhenderson Avatar answered Nov 03 '22 08:11

joelparkerhenderson