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

H

Henry Jones

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...
 
R

Rob Biedenharn

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...


15.25 in decimal
is exactly
1111.01 in binary

Seems like a finite number of bits to me ;-) However, the
construction of 15.25 as a literal is likely something roughly (1*10^1
+ 5*10^0) + (2*10^-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 Biedenharn http://agileconsultingllc.com
(e-mail address removed)
 
R

Ranieri Teixeira

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 o=
r
raw comparisons with assert and "=3D=3D".

I hope it help.

--=20
Ranieri Barros Teixeira
Ci=EAncia da Computa=E7=E3o - Faculdade de Computa=E7=E3o - Universidade F=
ederal do
Par=E1 (UFPA)
http://rubyxchart.rubyforge.org
http://rubytags.blogspot.com
 
M

Matthew Moss

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.
 
H

Henry Jones

Rob said:
15.25 in decimal
is exactly
1111.01 in binary

Seems like a finite number of bits to me ;-) However, the
construction of 15.25 as a literal is likely something roughly (1*10^1
+ 5*10^0) + (2*10^-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 Biedenharn http://agileconsultingllc.com
(e-mail address removed)


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)
 
R

Rob Biedenharn

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)


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 ;-)

-Rob

Rob Biedenharn http://agileconsultingllc.com
(e-mail address removed)
 
R

Rodrigo Kochenburger

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)

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 ;-)

-Rob

Rob Biedenharn http://agileconsultingllc.com
(e-mail address removed)

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

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

This is from a 32bits:
=> "15.000000000000000000000000000000"

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

Rob Biedenharn

Rob Biedenharn 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 ;-) However, the
construction of 15.25 as a literal is likely something roughly
(1*10^1
+ 5*10^0) + (2*10^-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 Biedenharn http://agileconsultingllc.com
(e-mail address removed)
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)

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 ;-)

-Rob

Rob Biedenharn http://agileconsultingllc.com
(e-mail address removed)

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

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

This is from a 32bits:
=> "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 Biedenharn http://agileconsultingllc.com
(e-mail address removed)
 
R

Rodrigo Kochenburger

Hi,

At Fri, 8 Feb 2008 22:35:04 +0900,
Rodrigo Kochenburger wrote in [ruby-talk:290370]:
I agree this is a problem with BigDecimal. The problem is not with the
floating point representation:
=> "15.000000000000001776356839400250"
=> "15.000000000000001776356839400250"
=> "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

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

Paolo Bonzini

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
 
P

Paolo Bonzini

Seems like a finite number of bits to me ;-) However, the
construction of 15.25 as a literal is likely something roughly (1*10^1
+ 5*10^0) + (2*10^-1 + 5*10^-2) and those intermediate fractional
terms are problematic.

It can be computed as (1*10^3 + 5*10^2 + 2*10^1 + 5*10^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
 
P

Paolo Bonzini

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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,283
Messages
2,571,405
Members
48,098
Latest member
inno vation

Latest Threads

Top