Arithmetic oddity

M

Minh Tran

l = [1.0, 1.0, 2.0**0.5]
=> [1.0, 1.0, 1.4142135623731]
s = l.map {|e| e**2} => [1.0, 1.0, 2.0]
puts 'foo' if s[2] == s[1] + s[0] => nil
puts 'foo' if 2.0 == 1.0 + 1.0
foo
=> nil

Does anyone know why s[2] == s[1] + s[0] is false ?
 
E

Eleanor McHugh

l = [1.0, 1.0, 2.0**0.5] => [1.0, 1.0, 1.4142135623731]
s = l.map {|e| e**2} => [1.0, 1.0, 2.0]
puts 'foo' if s[2] == s[1] + s[0] => nil
puts 'foo' if 2.0 == 1.0 + 1.0
foo
=> nil

Does anyone know why s[2] == s[1] + s[0] is false ?

If you check in IRB you'll find that ((2.0 ** 0.5) ** 2.0) == 2.0
returns false. That will be due to rounding errors in performing the
sequence of floating point operations.


Ellie

Eleanor McHugh
Games With Brains
http://slides.games-with-brains.net
 
M

Minh Tran

It still look odd as the value in s[2] is 2.0 which doesn't seems to
have any rounding problem.

In any case is there any workaround to fix get the condition as true ?


Eleanor said:
Does anyone know why s[2] == s[1] + s[0] is false ?
If you check in IRB you'll find that ((2.0 ** 0.5) ** 2.0) == 2.0
returns false. That will be due to rounding errors in performing the
sequence of floating point operations.


Ellie

Eleanor McHugh
Games With Brains
http://slides.games-with-brains.net
 
E

Eleanor McHugh

It still look odd as the value in s[2] is 2.0 which doesn't seems to
have any rounding problem.

In any case is there any workaround to fix get the condition as true ?

Floating point representations are in many cases (such as irrational
numbers like square root of 2) only approximations to a given value,
you therefore have to test that the value is accurate to a given
precision (number of significant digits). For example, given:

x = (1.0 + 1.0) ** 0.5

you could write your test as:

(x ** 2.0).between?(1.9999, 2.0001)


Ellie

Eleanor McHugh
Games With Brains
http://slides.games-with-brains.net
 
M

Minh Tran

I tried with

s = l.map {|e| (e**2).to_s.to_f}

instead of

s = l.map {|e| e**2}

and it now works.

It doesn't look very elegant though to need such kind of hacking to make
it base operator to work as expected.



Eleanor said:
It still look odd as the value in s[2] is 2.0 which doesn't seems to
have any rounding problem.

In any case is there any workaround to fix get the condition as true ?

Floating point representations are in many cases (such as irrational
numbers like square root of 2) only approximations to a given value,
you therefore have to test that the value is accurate to a given
precision (number of significant digits). For example, given:

x = (1.0 + 1.0) ** 0.5

you could write your test as:

(x ** 2.0).between?(1.9999, 2.0001)


Ellie

Eleanor McHugh
Games With Brains
http://slides.games-with-brains.net
 
E

Eleanor McHugh

I tried with

s = l.map {|e| (e**2).to_s.to_f}

instead of

s = l.map {|e| e**2}

and it now works.

It doesn't look very elegant though to need such kind of hacking to
make
it base operator to work as expected.

And that method also imposes considerable runtime cost. As I said, use
a comparison such as (2.0).between?(1.99999, 2.00001) as that's:

a. computationally much less expensive;
b. what a floating-point '==' actually means.

Also you should probably read http://en.wikipedia.org/wiki/Floating_point
and then do some further research into floating point number
representations to gain some deeper insight into what is actually
meant by 2.0 when it's converted into binary format. It may inspire
you to more elegant ways of solving whatever problem it is that you're
working on.


Ellie

Eleanor McHugh
Games With Brains
http://slides.games-with-brains.net
 
R

Robert Klemme

2008/7/2 Minh Tran said:
I tried with

s = l.map {|e| (e**2).to_s.to_f}

instead of

s = l.map {|e| e**2}

and it now works.

But it works only accidentally! This is likely to break soon again.
It doesn't look very elegant though to need such kind of hacking to make
it base operator to work as expected.

No, no, no! The operator works as expected. There is even an IEEE
standard defining how floating point math has to behave. You must
*never* rely on == when doing float math. In this case you rather
need to do either of these

- resort to decimal math with BigDecimal

- define equality as a max difference ("epsilon") between two float
values and test that

Kind regards

robert
 
E

Eleanor McHugh

But it works only accidentally! This is likely to break soon again.


No, no, no! The operator works as expected. There is even an IEEE
standard defining how floating point math has to behave. You must
*never* rely on == when doing float math. In this case you rather
need to do either of these

- resort to decimal math with BigDecimal

- define equality as a max difference ("epsilon") between two float
values and test that

Phrased much better than my replies :)


Ellie

Eleanor McHugh
Games With Brains
http://slides.games-with-brains.net
 

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

No members online now.

Forum statistics

Threads
474,202
Messages
2,571,057
Members
47,665
Latest member
salkete

Latest Threads

Top