[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Arithmetic oddity

Minh Tran

7/2/2008 11:30:00 AM

>> 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 ?
--
Posted via http://www.ruby-....

7 Answers

Eleanor McHugh

7/2/2008 12:05:00 PM

0

On 2 Jul 2008, at 12:30, Minh Tran wrote:
>>> 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-...
----
raise ArgumentError unless @reality.responds_to? :reason



Minh Tran

7/2/2008 12:49:00 PM

0

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 McHugh wrote:
> On 2 Jul 2008, at 12:30, Minh Tran wrote:
>> 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-...
> ----
> raise ArgumentError unless @reality.responds_to? :reason

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

Eleanor McHugh

7/2/2008 1:13:00 PM

0

On 2 Jul 2008, at 13:48, Minh Tran wrote:
> 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-...
----
raise ArgumentError unless @reality.responds_to? :reason



Minh Tran

7/2/2008 1:26:00 PM

0

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 McHugh wrote:
> On 2 Jul 2008, at 13:48, Minh Tran wrote:
>> 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-...
> ----
> raise ArgumentError unless @reality.responds_to? :reason

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

Eleanor McHugh

7/2/2008 1:57:00 PM

0

On 2 Jul 2008, at 14:25, Minh Tran wrote:
> 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/Floa...
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-...
----
raise ArgumentError unless @reality.responds_to? :reason



Robert Klemme

7/2/2008 2:02:00 PM

0

2008/7/2 Minh Tran <mtgred@gmail.com>:
> 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

--
use.inject do |as, often| as.you_can - without end

Eleanor McHugh

7/2/2008 2:06:00 PM

0

On 2 Jul 2008, at 15:01, Robert Klemme wrote:
> 2008/7/2 Minh Tran <mtgred@gmail.com>:
>> 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

Phrased much better than my replies :)


Ellie

Eleanor McHugh
Games With Brains
http://slides.games-with-...
----
raise ArgumentError unless @reality.responds_to? :reason