Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby float comparsion wrong? [duplicate]

I have an ActiveRecord object:

 s = Show.find 3980

Show has a lng column of type Decimal (precision 10, scale 7). If I call lng on s:

s.lng
#=> #<BigDecimal:7fac9a12ff40,'-0.821975E2',18(18)> 

all is good. But if I do this:

s.lng == -82.1975
#=> false

It returns false! But both of them are the same! Does it have something to do with my database column as decimal?

like image 475
0xSina Avatar asked Mar 13 '26 22:03

0xSina


1 Answers

Don't compare FPU decimal string fractions for equality

So, on 1.8.7p330:

BigDecimal.new('-82.1975') == -82.1975
 => true

But on 1.9.3p194 it's false.

The problem is that the comparison of a floating or double value with a decimal constant that contains a fraction is unreliable at best.

Very few decimal string fractions have exact values in the binary FP representation.* For example, between 0.01 and 0.99, only 0.25, 0.50, and 0.75 have exact binary representations that don't repeat. This is about half of the reason that BigDecimal even exists. (The other half of the reason is the fixed precision of FP data.)

So, the result that you get when comparing an actual machine value with a decimal string constant depends on every single bit in the binary fraction ... down to 1/252 ... and even then requires rounding.

If there is anything even the slightest bit (hehe, bit, sorry) imperfect about the process that produced the number, or the input conversion code, or anything else involved, they won't look exactly equal.

An argument could be made that the comparison should always fail because no IEEE-format FPU can even represent that number exactly.

MySQL and ActiveRecord have changed, over the years, the exact way numbers with fractions are handled.

But for sure you need to not mix floats with BigDecimal, i.e., choose rather:

BigDecimal.new('-82.1975')

and compare against that.


*The problem: machine numbers are x/2n, but decimal constants are x/(2n * 5m).

like image 145
DigitalRoss Avatar answered Mar 15 '26 15:03

DigitalRoss