Forum: Ruby Behavior of 0 and 0.0...

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Ed280b073cbe589e18e9f763e998cfdf?d=identicon&s=25 Raphael Clancy (puffer_ape)
on 2009-01-09 21:52
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!
E7559e558ececa67c40f452483b9ac8c?d=identicon&s=25 Gary Wright (Guest)
on 2009-01-09 22:23
(Received via mailing list)
On Jan 9, 2009, at 3:51 PM, Raphael Clancy wrote:

> 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
47b1910084592eb77a032bc7d8d1a84e?d=identicon&s=25 Joel VanderWerf (Guest)
on 2009-01-09 22:29
(Received via mailing list)
Gary Wright wrote:
> 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.
Ef3aa7f7e577ea8cd620462724ddf73b?d=identicon&s=25 Rob Biedenharn (Guest)
on 2009-01-09 22:29
(Received via mailing list)
On Jan 9, 2009, at 4:22 PM, Gary Wright wrote:

> 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
Rob@AgileConsultingLLC.com
Ed280b073cbe589e18e9f763e998cfdf?d=identicon&s=25 Raphael Clancy (puffer_ape)
on 2009-01-09 23:28
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/floatin...

On the other hand, the effort of re-doing the whole math framework might
not be worth the return...
465c65e84721ca2b833d5539f2d1a5d2?d=identicon&s=25 Jan Dvorak (Guest)
on 2009-01-10 00:11
(Received via mailing list)
On Friday 09 January 2009 23:27:46 Raphael Clancy wrote:
> 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
A61ecce13ed142622f24a5ca3a123922?d=identicon&s=25 Matthew Moss (Guest)
on 2009-01-10 00:24
(Received via mailing list)
On Jan 9, 2009, at 4:27 PM, Raphael Clancy wrote:
>
> 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/floatin...

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.
Ed280b073cbe589e18e9f763e998cfdf?d=identicon&s=25 Raphael Clancy (puffer_ape)
on 2009-01-10 05:14
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
C88ad33f428f88c72f6aab47971564af?d=identicon&s=25 Klaus Stein (Guest)
on 2009-01-10 10:15
(Received via mailing list)
Raphael Clancy <raphael.clancy@gmail.com> wrote:
>
> [...] 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
--
http://lapiz.istik.de/

The Answer is 42. And I am the Answer. Now I am looking for the
Question.
Ed280b073cbe589e18e9f763e998cfdf?d=identicon&s=25 Raphael Clancy (puffer_ape)
on 2009-01-10 15:23
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!
465c65e84721ca2b833d5539f2d1a5d2?d=identicon&s=25 Jan Dvorak (Guest)
on 2009-01-10 16:17
(Received via mailing list)
On Saturday 10 January 2009 15:23:12 Raphael Clancy wrote:
> 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:

>>> 1.0/0.0
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 :olddiv :/
  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
9b905791cbdbb1af35b65e02c3217e23?d=identicon&s=25 Tom Link (Guest)
on 2009-01-10 16:32
(Received via mailing list)
> 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.
9d751d4cd704a4efe3a335c77c4487a3?d=identicon&s=25 Dave Bass (dogsbody)
on 2009-01-12 12:44
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
9b905791cbdbb1af35b65e02c3217e23?d=identicon&s=25 Tom Link (Guest)
on 2009-01-12 14:30
(Received via mailing list)
> 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.
Ed280b073cbe589e18e9f763e998cfdf?d=identicon&s=25 Raphael Clancy (puffer_ape)
on 2009-01-13 16:28
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...
Ef3aa7f7e577ea8cd620462724ddf73b?d=identicon&s=25 Rob Biedenharn (Guest)
on 2009-01-13 17:19
(Received via mailing list)
On Jan 13, 2009, at 10:27 AM, Raphael Clancy wrote:

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

Actually, there are both positive 0 and negative 0 representations in
the IEEE spec.  They are considered equal (-0.0 == +0.0).  I believe
that FORTRAN has both -0.0 and +0.0, too.  There is no ε (epsilon) for
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
> 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".

Read the spec.  There are actually two different types of NaN -- one
that indicates an indeterminate value (quiet NaN or QNaN) and one that
indicates an operation is not defined (signaling NaN or SNan).

> Posted via http://www.ruby-forum.com/.
There are similar conventions that give things like 0 raised to the
0th power is 1, but 0 raised to any non-zero power is 0.  (You can try
that one in Ruby, too.)

-Rob

Rob Biedenharn    http://agileconsultingllc.com
Rob@AgileConsultingLLC.com
This topic is locked and can not be replied to.