nmat
nmat

Reputation: 7591

BigDecimal loses precision after multiplication

I'm getting a strange behaviour with BigDecimal in ruby. Why does this print false?

require 'bigdecimal'

a = BigDecimal.new('100')
b = BigDecimal.new('5.1')
c = a / b
puts c * b == a #false

Upvotes: 3

Views: 1344

Answers (2)

Neil Slater
Neil Slater

Reputation: 27207

This is normal behaviour, and not at all strange.

BigDecimal does not guarantee infinite accuracy, it allows you to specify arbitrary accuracy, which is not the same thing. The value 100/5.1 cannot be expressed with complete precision using floating point internal representation. Doesn't matter how many bits are used.

A "big rational" approach could achieve it - but would not give you access to some functions e.g. square roots.

See http://ruby-doc.org/core-1.9.3/Rational.html

# require 'rational' necessary only in Ruby 1.8
a = 100.to_r
b = '5.1'.to_r
c = a / b
c * b == a
# => true

Upvotes: 2

mu is too short
mu is too short

Reputation: 434665

BigDecimal doesn't claim to have infinite precision, it just provides support for precisions outside the normal floating point ranges:

BigDecimal provides similar support for very large or very accurate floating point numbers.

But BigDecimal values still have a finite number of significant digits, hence the precs method:

precs

Returns an Array of two Integer values.

The first value is the current number of significant digits in the BigDecimal. The second value is the maximum number of significant digits for the BigDecimal.

You can see things starting to go awry if you look at your c:

>> c.to_s
=> "0.19607843137254901960784313725E2"

That's a nice clean rational number but BigDecimal doesn't know that, it is still stuck seeing c as a finite string of digits.

If you use Rational instead, you'll get the results you're expecting:

>> a = Rational(100)
>> b = Rational(51, 10)
>> c * b == a
=> true

Of course, this trickery only applies if you are working with Rational numbers so anything fancy (such as roots or trigonometry) is out of bounds.

Upvotes: 3

Related Questions