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

···

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

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 :wink: 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 ===

class Float
  def ===(other,eps=0.000000001)
    (self - other.to_f).abs < eps
    end
  end

=> nil

require 'bigdecimal'

=> true

15.25 == BigDecimal.new("15.25")

=> false

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
Rob@AgileConsultingLLC.com

···

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

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.

···

--
Ranieri Barros Teixeira
Ciência da Computação - Faculdade de Computação - Universidade Federal do
Pará (UFPA)
http://rubyxchart.rubyforge.org
http://rubytags.blogspot.com

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

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.

Rob Biedenharn 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
+ 5*10^0) + (2*10^-1 + 5*10^-2) and those intermediate fractional
terms are problematic.

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

> class Float
> def ===(other,eps=0.000000001)
> (self - other.to_f).abs < eps
> end
> end
=> nil
> require 'bigdecimal'
=> true
> 15.25 == BigDecimal.new("15.25")
=> false
> 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
Rob@AgileConsultingLLC.com

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 5:26 PM, Henry Jones wrote:

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

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

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.

···

On Feb 7, 6:17 pm, Matthew Moss <matthew.m...@gmail.com> 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.

Rob Biedenharn 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
+ 5*10^0) + (2*10^-1 + 5*10^-2) and those intermediate fractional
terms are problematic.

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

> class Float
> def ===(other,eps=0.000000001)
> (self - other.to_f).abs < eps
> end
> end
=> nil
> require 'bigdecimal'
=> true
> 15.25 == BigDecimal.new("15.25")
=> false
> 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
Rob@AgileConsultingLLC.com

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

"%.30f" % 15.25

=> "15.250000000000000000000000000000"

require 'bigdecimal'

=>

"%.30f" % BigDecimal.new('15.25')

=> "15.250000000000001776356839400250"

"%.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:

"%.30f" % BigDecimal.new('0.05')

=> "0.050000000000000002775557561563"

"%.30f" % BigDecimal.new('0.2')

=> "0.200000000000000011102230246252"

"%.30f" % BigDecimal.new('5.0')

=> "5.000000000000000000000000000000"

"%.30f" % BigDecimal.new('10.0')

=> "10.000000000000000000000000000000"

(and your '2' is off by two places :wink:

-Rob

Rob Biedenharn http://agileconsultingllc.com
Rob@AgileConsultingLLC.com

···

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

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

> Rob Biedenharn 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
>> + 5*10^0) + (2*10^-1 + 5*10^-2) and those intermediate fractional
>> terms are problematic.

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

>> > class Float
>> > def ===(other,eps=0.000000001)
>> > (self - other.to_f).abs < eps
>> > end
>> > end
>> => nil
>> > require 'bigdecimal'
>> => true
>> > 15.25 == BigDecimal.new("15.25")
>> => false
>> > 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
>> R...@AgileConsultingLLC.com

> 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)
> --
> Posted viahttp://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.

> "%.30f" % 15.25
=> "15.250000000000000000000000000000"
> require 'bigdecimal'
=>
> "%.30f" % BigDecimal.new('15.25')
=> "15.250000000000001776356839400250"
> "%.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:

> "%.30f" % BigDecimal.new('0.05')
=> "0.050000000000000002775557561563"
> "%.30f" % BigDecimal.new('0.2')
=> "0.200000000000000011102230246252"
> "%.30f" % BigDecimal.new('5.0')
=> "5.000000000000000000000000000000"
> "%.30f" % BigDecimal.new('10.0')
=> "10.000000000000000000000000000000"

(and your '2' is off by two places :wink:

-Rob

Rob Biedenharn http://agileconsultingllc.com
R...@AgileConsultingLLC.com

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 7, 10:42 pm, Rob Biedenharn <R...@AgileConsultingLLC.com> wrote:

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

Rob Biedenharn 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
+ 5*10^0) + (2*10^-1 + 5*10^-2) and those intermediate fractional
terms are problematic.

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

> class Float
> def ===(other,eps=0.000000001)
> (self - other.to_f).abs < eps
> end
> end
=> nil
> require 'bigdecimal'
=> true
> 15.25 == BigDecimal.new("15.25")
=> false
> 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
R...@AgileConsultingLLC.com

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)
--
Posted viahttp://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.

> "%.30f" % 15.25
=> "15.250000000000000000000000000000"
> require 'bigdecimal'
=>
> "%.30f" % BigDecimal.new('15.25')
=> "15.250000000000001776356839400250"
> "%.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:

> "%.30f" % BigDecimal.new('0.05')
=> "0.050000000000000002775557561563"
> "%.30f" % BigDecimal.new('0.2')
=> "0.200000000000000011102230246252"
> "%.30f" % BigDecimal.new('5.0')
=> "5.000000000000000000000000000000"
> "%.30f" % BigDecimal.new('10.0')
=> "10.000000000000000000000000000000"

(and your '2' is off by two places :wink:

-Rob

Rob Biedenharn http://agileconsultingllc.com
R...@AgileConsultingLLC.com

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?

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.

require 'bigdecimal'

=> true

"%.30f" % (61.0/4.0)

=> "15.250000000000000000000000000000"

(61.0/4.0) == 15.25

=> true

"%.30f" % 15.25

=> "15.250000000000000000000000000000"

"%.30f" % BigDecimal.new("15.25")

=> "15.250000000000001776356839400250"

"%.30f" % BigDecimal.new("15.25").to_s.to_f

=> "15.250000000000000000000000000000"

-Rob

Rob Biedenharn http://agileconsultingllc.com
Rob@AgileConsultingLLC.com

···

On Feb 8, 2008, at 8:35 AM, Rodrigo Kochenburger wrote:

On Feb 7, 10:42 pm, Rob Biedenharn <R...@AgileConsultingLLC.com> > wrote:

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

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

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:

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

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.

···

On Feb 8, 12:43 pm, Nobuyoshi Nakada <n...@ruby-lang.org> wrote:

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:

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

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

--
Nobu Nakada

> 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