Behavior of 0 and 0.0...

R

Raphael Clancy

I was playing around with the basic math functions, and I had some
questions about the way Ruby handles operations with 0 and 0.0.

first we have:

$ ruby -v
ruby 1.8.6 (2007-09-24 patchlevel 111) [i486-linux]
$ irb
irb(main):001:0> 0/0
ZeroDivisionError: divided by 0
from (irb):1:in `/'
from (irb):1

This is OK, it lets us know that we made a mistake somewhere, but when
we try 0.0/0.0 we get:

irb(main):002:0> 0.0/0.0
=> NaN

Mathematically, this is preferable to division error, but, maybe not
from a programming standpoint? The question here is why should these two
events generate different results?

Now, if we try something like 4.0/0.0 we get, what I would consider,
really weird behavior:

irb(main):003:0> 4.0/0.0
=> Infinity

It is true that as x approaches 0 the limit of 1/x goes to infinity, but
this is not the same as 1/0 = infinity. In this case why would infinity
be preferable to the simpler result, NaN? At first I thought this might
be a precision error, that is the parser is saying that 0.0 is not
really 0, just very close. But, if that was the case then 0.0/0.0 would
be 1 instead of NaN.

Thanks!
 
G

Gary Wright

[Note: parts of this message were removed to make it a legal post.]


I was playing around with the basic math functions, and I had some
questions about the way Ruby handles operations with 0 and 0.0.

I'm guessing that you are seeing IEEE floating point behavior with
the floating point operations and that there isn't a perfect analog
to that behavior with integer operations. IEEE floating
point defines binary representations of negative and positive zero,
negative and positive infinity, and two types of NaN (not a number).
These 'numbers' aren't available when working with integers at the
machine level nor at the Ruby Fixnum or Bignum level.

Gary Wright
 
J

Joel VanderWerf

Gary said:
I'm guessing that you are seeing IEEE floating point behavior with
the floating point operations and that there isn't a perfect analog
to that behavior with integer operations. IEEE floating
point defines binary representations of negative and positive zero,
negative and positive infinity, and two types of NaN (not a number).
These 'numbers' aren't available when working with integers at the
machine level nor at the Ruby Fixnum or Bignum level.

Gary Wright

And just to underline the point... these behaviors are not ruby spec,
but rather IEEE fp spec.
 
R

Rob Biedenharn

I'm guessing that you are seeing IEEE floating point behavior with
the floating point operations and that there isn't a perfect analog
to that behavior with integer operations. IEEE floating
point defines binary representations of negative and positive zero,
negative and positive infinity, and two types of NaN (not a number).
These 'numbers' aren't available when working with integers at the
machine level nor at the Ruby Fixnum or Bignum level.

Gary Wright

In particular, look at how IEEE defines operations such as 0.0/0.0

http://steve.hollasch.net/cgindex/coding/ieeefloat.html

-Rob

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

Jan Dvorak

Thanks for the quick replies (and the cool link!). I thought this might
be a hold over from the "good old days". ;)

I wonder if there is any impetus to move beyond this, since I see that
sticking to the IEEE might cause other problems as well. (Warning,
grumpy blog ahead)

http://infinitesecond.blogspot.com/2008/03/floating-point-arithmetic-bug-re
ports.html

On the other hand, the effort of re-doing the whole math framework might
not be worth the return...

Programming languages generally 'sticks' to IEEE 754 simply because that's
what the hardware can do. You take the numbers and operator, pass it to the
CPU (or more precise to CPU's math coprocessor) and you'll get output number
(or NaN,+Inf, etc.). Doing floating point in software is possible if you really
want to (and there are scientific libraries for precise real number
calculations that do that), but it is not something you would *like* to do as
common case, performance-wise.

Jan
 
M

Matthew Moss

I wonder if there is any impetus to move beyond this, since I see that
sticking to the IEEE might cause other problems as well. (Warning,
grumpy blog ahead)

http://infinitesecond.blogspot.com/2008/03/floating-point-arithmetic-bug-reports.html

Written by someone who does not understand floating point arithmetic.

As Jan Dvorak indicated in another message, most languages stick to
IEEE spec because the hardware implements the spec. And with good
reason: it's a good spec.

Your blogger is upset that things don't work the way he wants; perhaps
he should create a new FP spec and implement it. I guarantee you --
even ignoring the hardware implemented IEEE -- he won't make a
representation as complete as IEEE.
 
R

Raphael Clancy

I agree that the IEEE spec is closer to what the FPU is doing (actually,
it is what the FPU is doing ;-D), and this surely gives it a speed
advantage. But honestly, if speed was what I was after, Ruby might not
be my first choice. Also, I'm not sure that simply being closer to the
hardware is always a good thing. After all, I'm pretty glad that Ruby
doesn't force me to use things like pointers or malloc(), even though
that's much closer to how the MMU works. The IEEE spec also has one big
strike against it, it's representation of how numbers work is (just
slightly) wrong. It's a hold over from the days when computers couldn't
do any better, but we can certainly do better now.

All that being said, I can come up with several reasons why we should
keep the IEEE spec. First, it's "traditional" and Ruby always tries to
work in the way programmers expect it should. Second, dropping the IEEE
spec in favor of something more mathematically correct would very likely
break a lot of things that depend on Floats working the IEEE way. And,
finally, who cares if the IEEE spec is broken? The number of people it
affects is tiny, and they are likely to use higher precision math
libraries anyway. The work needed to fix it would far out weigh the
benefit.

So I guess I'm all for keeping the spec, IEEE is practical, but it
doesn't seem very "Ruby". ;-D
 
K

Klaus Stein

Raphael Clancy said:
[...] I can come up with several reasons why we should
keep the IEEE spec. First, it's [ ... ]

One important reason was not mentioned here: In floating point processing we
have limited precision which leads to different semantics. Doing integer
arithmetics 5 is 5, 1 is 1 and 0 is 0. In floating point arithmetics 5.0,
1.0 and 0.0 represent an interval. See the following in irb (on a 32 bit
Linux box):

irb(main):007:0> 10**-200/10.0**200
=> 0.0
irb(main):008:0> -10**-200/10.0**200
=> -0.0
irb(main):009:0> 42.23 / (10**-200/10.0**200)
=> Infinity
irb(main):010:0> 42.23 / (-10**-200/10.0**200)
=> -Infinity
irb(main):011:0> (10**200/10.0**-200)
=> Infinity
....

That's simply the closest we can get with limited precision.

Floating point arithmetics _differs_ from doing math on R, and it's
important to know this:

irb(main):031:0> 0.1**2 == 0.01
=> false
irb(main):032:0> 0.1**2 - 0.01
=> 1.73472347597681e-18
irb(main):033:0> (0.1**2 - 0.01) < 1e-9
=> true
So I guess I'm all for keeping the spec, IEEE is practical, but it
doesn't seem very "Ruby". ;-D

You could also claim floating point arithmetics is not very "Ruby2...

Klaus
 
R

Raphael Clancy

I've had a little time to think on this (and a little sleep ;-D) and I
realize that I got sidetracked by the precision issue and it really
isn't what bothers me. I know that precision has been the focus of most
of the grumping about the IEEE spec, but keeping track of precision is
part of using a computer. what bothers me it that using the IEEE spec
causes Ruby to handle division by zero inconsistently and incorrectly.
While the precision issue is part of the hardware, but the division by
zero stuff is a design choice. I realize that the IEEE doesn't set out
these specs arbitrarily and there must be many good reasons for this
behavior. But, it seems inelegant to me.

Like I said before, Ruby should stick with the IEEE spec, switching away
would be a huge hassle, and the benefits would be negligible. But, that
doesn't mean we shouldn't acknowledge that the IEEE spec has some
issues. And that even though it's been "good enough" for long time, at
some point in the future that could possibly change.

Thanks for the good discussion!
 
J

Jan Dvorak

I've had a little time to think on this (and a little sleep ;-D) and I
realize that I got sidetracked by the precision issue and it really
isn't what bothers me. I know that precision has been the focus of most
of the grumping about the IEEE spec, but keeping track of precision is
part of using a computer. what bothers me it that using the IEEE spec
causes Ruby to handle division by zero inconsistently and incorrectly.
While the precision issue is part of the hardware, but the division by
zero stuff is a design choice. I realize that the IEEE doesn't set out
these specs arbitrarily and there must be many good reasons for this
behavior. But, it seems inelegant to me.

It's still coming from the hardware, you pass fdiv(x,0.0) to FPU and you get
floating point number back that is either NaN or Infinity. So the "design
choice" is actually "don't do any additional checking, let the hardware handle
it".

I realize that other script languages are more consisent about throwing
exception, eg. in Python:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: float division

or Perl:
1.0/0.0
Illegal division by zero at - line 1.

and the same for other operations producing NaN (eg. sqrt(-1)). I don't know
how much of a good idea would be to follow the same way, but if you wish so,
you can always use monkey patching to add the checks you need, eg.:

class Float
alias :eek:lddiv :/
def /(y)
if (y==0)
raise ZeroDivisionError
else
self.olddiv(y)
end
end
end

p 1.0/0.0

$ruby test.rb
test.rb:7:in `/': ZeroDivisionError (ZeroDivisionError)
from test.rb:14


Jan
 
T

Tom Link

eg. in Python:
or Perl:
1.0/0.0
Illegal division by zero at - line 1.

What would the equivalent of the following code return in perl or
python then?

a = 1.0; 50.times { a /= 1_000_000_000; p 1.0 / a }

What would you expect to be the result of:

p 1.0 / 0.1 < 1.0 / a

In ruby, it's true.

Maybe it helps to think of 0.0 as 0.0 + eta with eta being too small
to be represented, maybe not. The integer 0 is just 0 and x / 0 is not
a number.
 
D

Dave Bass

I learnt a long time ago (probably in Fortran) that it's always a good
idea to check the value of a divisor before you use it in a division.
Now doing that comes as second nature.

In Ruby I'd raise an exception. Or if I'm actually expecting to get a
lot of zeroes and they're not really errors but valid data (e.g. someone
has used them to mark incomplete data or similar) then I'd take
appropriate action in place.

Relying on IEEE results is likely to cause problems downstream, so it's
best to catch these things at the earliest opportunity IMHO. (A bit like
checking for null pointers in C.)

Dave
 
T

Tom Link

Relying on IEEE results is likely to cause problems downstream, so it's
best to catch these things at the earliest opportunity IMHO.

You're probably right. This thread made me wonder though if I'd
actually consider a division by zero error more ruby-like as the OP
seemed to suggest. BTW in the meantime, I also checked maple and R and
they both report 1.0/0.0 as infinity.
 
R

Raphael Clancy

I think this is probably is a case of 0.0 is not really 0. After all, as
much as I might wish it were, a float is not a real. What we really have
is the case mentioned before, that is 0.0 is really 0 + eta, where eta
is some very small number which represents a precision error. Earlier, I
was thrown off by the 0.0/0.0 case, because that would really be (0 +
eta)/(0 + eta'), and since eta and eta' have similar magnitude this
value should be around 1, even though we can't know what the number is,
it certainly in a number. I'm not sure why the IEEE chose to define this
case as NaN, I think it must have been shorthand for "undefined".

As an interesting (to nerds ;-D) aside, there are several systems in
which 1/0 is infinity, but they usually depend on fancy geometrical
tricks, like making the reals occupy a circle or some more complicated
form instead of a line. So that +infinity == -infinity. That way you can
make the function f(x)=1/x be "well" defined everywhere. I don't like
it, but, plenty of people do...
 
R

Rob Biedenharn

I think this is probably is a case of 0.0 is not really 0. After =20
all, as
much as I might wish it were, a float is not a real. What we really =20=
have
is the case mentioned before, that is 0.0 is really 0 + eta, where eta
is some very small number which represents a precision error.

Actually, there are both positive 0 and negative 0 representations in =20=

the IEEE spec. They are considered equal (-0.0 =3D=3D +0.0). I believe =
=20
that FORTRAN has both -0.0 and +0.0, too. There is no =CE=B5 (epsilon) =
for =20
0.0.
Earlier, I
was thrown off by the 0.0/0.0 case, because that would really be (0 +
eta)/(0 + eta'), and since eta and eta' have similar magnitude this
value should be around 1, even though we can't know what the number =20=
is,
it certainly in a number. I'm not sure why the IEEE chose to define =20=
this
case as NaN, I think it must have been shorthand for "undefined".

Read the spec. There are actually two different types of NaN -- one =20
that indicates an indeterminate value (quiet NaN or QNaN) and one that =20=

indicates an operation is not defined (signaling NaN or SNan).
As an interesting (to nerds ;-D) aside, there are several systems in
which 1/0 is infinity, but they usually depend on fancy geometrical
tricks, like making the reals occupy a circle or some more complicated
form instead of a line. So that +infinity =3D=3D -infinity. That way = you =20
can
make the function f(x)=3D1/x be "well" defined everywhere. I don't = like
it, but, plenty of people do...
--=20
Posted via http://www.ruby-forum.com/.

There are similar conventions that give things like 0 raised to the =20
0th power is 1, but 0 raised to any non-zero power is 0. (You can try =20=

that one in Ruby, too.)

-Rob

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

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
473,764
Messages
2,569,564
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top