Arithmetic oddity

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 ?

On 2 Jul 2008, at 12:30, Minh T. 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-brains.net

raise ArgumentError unless @reality.responds_to? :reason

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 T. 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-brains.net

raise ArgumentError unless @reality.responds_to? :reason

On 2 Jul 2008, at 13:48, Minh T. 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-brains.net

raise ArgumentError unless @reality.responds_to? :reason

On 2 Jul 2008, at 14:25, Minh T. wrote:

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

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

raise ArgumentError unless @reality.responds_to? :reason

2008/7/2 Minh T. [email protected]:

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

On 2 Jul 2008, at 15:01, Robert K. wrote:

  • 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 :slight_smile:

Ellie

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

raise ArgumentError unless @reality.responds_to? :reason

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 T. 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-brains.net

raise ArgumentError unless @reality.responds_to? :reason