BigDecimal.new('15.25') == 15.25, false?

Hi,

I was writing some unit tests when I ran into some troubles… In the end
I found out the problem :

BigDecimal.new(‘15.25’) == 15.25

returns false !!

BigDecimal.new(‘15.25’).to_f == 15.25.to_f also returns false.

Substracting both values returns 1.7763… e-015

This is on ruby 1.8.6, Win32.

I’m a comp. engineer, so I know it’s ultimately related to the binary
value of 15.25 which cannot be precisely represented (without an
infinite number of bits) , but I’m still amazed that such a simple
comparison with a literal value should fail…

On Feb 7, 2008, at 5:26 PM, Henry Jones wrote:

BigDecimal.new(‘15.25’).to_f == 15.25.to_f also returns false.

Substracting both values returns 1.7763… e-015

This is on ruby 1.8.6, Win32.

I’m a comp. engineer, so I know it’s ultimately related to the binary
value of 15.25 which cannot be precisely represented (without an
infinite number of bits) , but I’m still amazed that such a simple
comparison with a literal value should fail…

15.25 in decimal
is exactly
1111.01 in binary

Seems like a finite number of bits to me :wink: However, the
construction of 15.25 as a literal is likely something roughly (1*10^1

  • 510^0) + (210^-1 + 5*10^-2) and those intermediate fractional
    terms are problematic.

Hey, this is Ruby! Roll your own with ===

irb> class Float
irb> def ===(other,eps=0.000000001)
irb> (self - other.to_f).abs < eps
irb> end
irb> end
=> nil
irb> require ‘bigdecimal’
=> true
irb> 15.25 == BigDecimal.new(“15.25”)
=> false
irb> 15.25 === BigDecimal.new(“15.25”)
=> true

Of course, you’d have to add a similar BigDecimal#=== to get symmetry.

-Rob

Rob B. http://agileconsultingllc.com
[email protected]

Hi, Henry
I’m writing a Ruby app that need very precise float point arithmetic.
So,
I’m using BigDecimal and test my code with unit tests, at windows xp
with
Ruby 1.8.6, also.

This seems a problem with float point number representation and
comparison.

I wrote the following method, to test what you want to:

def test_big_decimal
assert_in_delta 15.25, BigDecimal(‘15.25’), 0.001
end

And… it pass. I’ve found this type here, at ruby-talk. Do a search for
a
message with “assert_equal problem” to see the reply.

Now, all my code in unit tests uses assert_in_delta, instead
assert_equal or
raw comparisons with assert and “==”.

I hope it help.

15.25 in decimal
is exactly
1111.01 in binary

No it’s not. It is 1111.01 in fixed point. Floating-point, however,
is significantly different.

On Feb 7, 6:17 pm, Matthew M. [email protected] wrote:

15.25 in decimal
is exactly
1111.01 in binary

No it’s not. It is 1111.01 in fixed point. Floating-point, however,
is significantly different.

Okay, I’m half-right and half-wrong. The floating-point rep is
different, but “significantly” may be a bit too much. =)

If my calculations are correct, 15.25 in binary is:
0 10000010 11101000000000000000000
S exponent mantissa

The 11101 at the front of the mantissa is most of the fixed point
rep. There is an implicit 1. The exponent is biased by +127.

Rob B. wrote:

On Feb 7, 2008, at 5:26 PM, Henry Jones wrote:

BigDecimal.new(‘15.25’).to_f == 15.25.to_f also returns false.

Substracting both values returns 1.7763… e-015

This is on ruby 1.8.6, Win32.

I’m a comp. engineer, so I know it’s ultimately related to the binary
value of 15.25 which cannot be precisely represented (without an
infinite number of bits) , but I’m still amazed that such a simple
comparison with a literal value should fail…

15.25 in decimal
is exactly
1111.01 in binary

Seems like a finite number of bits to me :wink: However, the
construction of 15.25 as a literal is likely something roughly (1*10^1

  • 510^0) + (210^-1 + 5*10^-2) and those intermediate fractional
    terms are problematic.

Hey, this is Ruby! Roll your own with ===

irb> class Float
irb> def ===(other,eps=0.000000001)
irb> (self - other.to_f).abs < eps
irb> end
irb> end
=> nil
irb> require ‘bigdecimal’
=> true
irb> 15.25 == BigDecimal.new(“15.25”)
=> false
irb> 15.25 === BigDecimal.new(“15.25”)
=> true

Of course, you’d have to add a similar BigDecimal#=== to get symmetry.

-Rob

Rob B. http://agileconsultingllc.com
[email protected]

Apparently the problem lies with BigDecimal, and not the literal value :

“%.30f” % 15.25
will print : “15.250000000000000000000000000000” (not copy pasted,
don’t count the zeros =) )

but
“%.30f” % BigDecimal(“15.25”)
will print : “15.250000000000002000000000000000” (notice the ‘2’ in
there)

On Feb 7, 2008, at 6:59 PM, Henry Jones wrote:

binary
construction of 15.25 as a literal is likely something roughly
irb> end

will print : “15.250000000000000000000000000000” (not copy pasted,
don’t count the zeros =) )

but
“%.30f” % BigDecimal(“15.25”)
will print : “15.250000000000002000000000000000” (notice the ‘2’ in
there)

Posted via http://www.ruby-forum.com/.

Well, now you’re pulling in the BigDecimal#to_f which isn’t so much a
problem in BigDecimal as it is the same limitation of a Float.

irb> “%.30f” % 15.25
=> “15.250000000000000000000000000000”
irb> require ‘bigdecimal’
=> []
irb> “%.30f” % BigDecimal.new(‘15.25’)
=> “15.250000000000001776356839400250”
irb> “%.30f” % BigDecimal.new(‘15.25’).to_f
=> “15.250000000000001776356839400250”

And that just gets back to the problem of building the number up from
its decimal pieces:

irb> “%.30f” % BigDecimal.new(‘0.05’)
=> “0.050000000000000002775557561563”
irb> “%.30f” % BigDecimal.new(‘0.2’)
=> “0.200000000000000011102230246252”
irb> “%.30f” % BigDecimal.new(‘5.0’)
=> “5.000000000000000000000000000000”
irb> “%.30f” % BigDecimal.new(‘10.0’)
=> “10.000000000000000000000000000000”

(and your ‘2’ is off by two places :wink:

-Rob

Rob B. http://agileconsultingllc.com
[email protected]

On Feb 7, 10:42 pm, Rob B. [email protected]
wrote:

1111.01 in binary
irb> def ===(other,eps=0.000000001)

=> “15.250000000000001776356839400250”
irb> “%.30f” % BigDecimal.new(‘10.0’)
=> “10.000000000000000000000000000000”

(and your ‘2’ is off by two places :wink:

-Rob

Rob B. http://agileconsultingllc.com
[email protected]

I agree this is a problem with BigDecimal. The problem is not with the
floating point representation:

“%.30f” % BigDecimal.new(“15.00”)
=> “15.000000000000001776356839400250”
“%.30f” % BigDecimal.new(“15.00”).to_f
=> “15.000000000000001776356839400250”
“%.30f” % 15.00
=> “15.000000000000000000000000000000”

Also, it looks like it happens on 64bits machines only.

This is from a 32bits:

“%.30f” % BigDecimal.new(“15.00”)
=> “15.000000000000000000000000000000”
“%.30f” % BigDecimal.new(“15.00”).to_f
=> “15.000000000000000000000000000000”
“%.30f” % 15.00
=> “15.000000000000000000000000000000”

Anybody familiar with the BigDecimal internals could check this,
please?

On Feb 8, 2008, at 8:35 AM, Rodrigo K. wrote:

This is on ruby 1.8.6, Win32.

irb> (self - other.to_f).abs < eps
Of course, you’d have to add a similar BigDecimal#=== to get
“%.30f” % 15.25
Well, now you’re pulling in the BigDecimal#to_f which isn’t so much a

=> “10.000000000000000000000000000000”

“%.30f” % BigDecimal.new(“15.00”)
=> “15.000000000000000000000000000000”
“%.30f” % BigDecimal.new(“15.00”).to_f
=> “15.000000000000000000000000000000”
“%.30f” % 15.00
=> “15.000000000000000000000000000000”

Anybody familiar with the BigDecimal internals could check this,
please?

Well, my machine is only 32bits. You shouldn’t have a problem with
small integers. It’s when you attempt to represent some fractional
decimal values in binary. Now, if you wanted to say something about
the apparent difference between BigDecimal#to_f and String#to_f, then
you could be onto something.

irb> require ‘bigdecimal’
=> true
irb> “%.30f” % (61.0/4.0)
=> “15.250000000000000000000000000000”
irb> (61.0/4.0) == 15.25
=> true
irb> “%.30f” % 15.25
=> “15.250000000000000000000000000000”
irb> “%.30f” % BigDecimal.new(“15.25”)
=> “15.250000000000001776356839400250”
irb> “%.30f” % BigDecimal.new(“15.25”).to_s.to_f
=> “15.250000000000000000000000000000”

-Rob

Rob B. http://agileconsultingllc.com
[email protected]

Hi,

At Fri, 8 Feb 2008 22:35:04 +0900,
Rodrigo K. wrote in [ruby-talk:290370]:

Also, it looks like it happens on 64bits machines only.
Could you try the patch in [ruby-dev:33658]?
http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-dev/33658

On Feb 8, 12:43 pm, Nobuyoshi N. [email protected] wrote:

“%.30f” % BigDecimal.new(“15.00”).to_f
=> “15.000000000000001776356839400250”
“%.30f” % 15.00
=> “15.000000000000000000000000000000”

Also, it looks like it happens on 64bits machines only.

Could you try the patch in [ruby-dev:33658]?http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-dev/33658


Nobu Nakada

Oh yeah baby. That seems to fix it.
Great work.

I’m a comp. engineer, so I know it’s ultimately related to the binary
value of 15.25 which cannot be precisely represented (without an
infinite number of bits)

Unfortunately no, it looks like a bug. Because 15.25 is 1111.01 in
binary – exactly.

Paolo

Also, it looks like it happens on 64bits machines only.

That could be because on 32-bits computations which don’t leave the
FPU are kept with 64 bits of mantissa. On 64-bits everything uses 53
bits because SSE is used for math. Of course this only applies to C
code, and may also depend on the C compiler. See
http://gcc.gnu.org/PR323
for the gory details.

Paolo

Seems like a finite number of bits to me :wink: However, the
construction of 15.25 as a literal is likely something roughly (1*10^1

  • 510^0) + (210^-1 + 5*10^-2) and those intermediate fractional
    terms are problematic.

It can be computed as (110^3 + 510^2 + 210^1 + 510^0) / 10^2 to
avoid those terms. I could bet that’s the bug.

Note that x / 10^2 is not the same as x * 10^-2; division is more
precise because it does not need an infinite number of bits to
represent the divisor.

Paolo, who’s relieved to see other people making the same mistakes as
him