Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Variable with NaN value is not equal to itself in Ruby

Tags:

equality

ruby

nan

I was testing some of mine Ruby 1.9 code when I've stumbled across something that is very interesting. I'm hoping that someone can explain why is this happening.

Here is the code:

inf = Float::INFINITY

x = inf - inf
y = 0.0/0.0

puts "X is #{x}"
puts "Y is #{y}"

puts "X and Y are both NaN." if x.nan? && y.nan?

puts "This is all as we expected, but here is the mystery..."

puts "X is not equal to Y." if x == y

puts "Surprisingly not even X is equal to X." if x == x

And this is the output:

X is NaN
Y is NaN
X and Y are both NaN.
This is all as we expected, but here is the mystery...

I have assigned a NaN value in two different ways to two variables and when I've tested if they were equal that was not the case. After that I've tested if one variable is equal to itself and even that wasn't true. It's logical to expect that some value is equal to itself but as we can se, it just isn't.

I don't know if this is the right way to test NaN values, but I would really like to know what is behind all of this and is there a reason for a Ruby to behave like this?

Thank you

like image 533
milan-j Avatar asked Feb 19 '23 22:02

milan-j


2 Answers

If we think about it, it seems logical. You get x by subtracting one infinitely large number from another infinitely large number? What's the result of that? We don't know.

How can we compare things that we don't know?

To backup my logic, here's the source of Float#==

static VALUE flo_eq(VALUE x, VALUE y)
{
    volatile double a, b;

    switch (TYPE(y)) {
      case T_FIXNUM:
        b = (double)FIX2LONG(y);
        break;
      case T_BIGNUM:
        b = rb_big2dbl(y);
        break;
      case T_FLOAT:
        b = RFLOAT_VALUE(y);
#if defined(_MSC_VER) && _MSC_VER < 1300
        if (isnan(b)) return Qfalse;
#endif
        break;
      default:
        return num_equal(x, y);
    }
    a = RFLOAT_VALUE(x);
#if defined(_MSC_VER) && _MSC_VER < 1300
    if (isnan(a)) return Qfalse;
#endif
    return (a == b)?Qtrue:Qfalse;
}
like image 110
Sergio Tulentsev Avatar answered Mar 04 '23 02:03

Sergio Tulentsev


Make the exact same code in Javascript:

x = Infinity - Infinity
y = 0/0
if(!(x==y)) alert("Not equal");
if(!(x==x)) alert("Not equal");

It will have the exact same behavior. And this behavior is expected. Two NaN's are not equal between themselves. Infinity - Infinity is not the same thing as [One huge number] - [The same huge number], they are two different huge numbers. Nobody knows the result of this operation. So, this is a NaN.

So, this is not a mistake. This is mathematically plausible.

Also, try

if(!(NaN==NaN)) alert("Not equal");
if(!(NaN===NaN)) alert("Not equal");

NaN will never be equal to anything.

like image 33
YuriAlbuquerque Avatar answered Mar 04 '23 02:03

YuriAlbuquerque