Переменная со значением NaN не равна самой себе в Ruby

Я тестировал свой код Ruby 1.9, когда наткнулся на кое-что очень интересное. Я надеюсь, что кто-то может объяснить, почему это происходит.

Вот код:

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

И это вывод:

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

Я присвоил значение NaN двумя разными способами двум переменным, и когда я проверил, равны ли они, это было не так. После этого я проверил, равна ли одна переменная самой себе, и даже это было неправдой. Логично ожидать, что какое-то значение равно самому себе, но, как мы видим, это не так.

Я не знаю, правильный ли это способ проверки значений NaN, но мне бы очень хотелось узнать, что стоит за всем этим, и есть ли причина, по которой Ruby ведет себя так?

Спасибо


person milan-j    schedule 05.07.2012    source источник
comment
Обратите внимание, что такое поведение не является специфичным для Ruby, а предписано стандартом IEEE с плавающей запятой.   -  person sepp2k    schedule 06.07.2012


Ответы (2)


Если подумать, то это кажется логичным. Вы получаете x, вычитая одно бесконечно большое число из другого бесконечно большого числа? Каков результат этого? Мы не знаем.

Как мы можем сравнивать то, чего не знаем?

Чтобы поддержать мою логику, вот источник 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;
}
person Sergio Tulentsev    schedule 05.07.2012
comment
Самое интересное в этом коде — директивы условной компиляции для возврата Qfalse в Windows. - person Michael Kohl; 06.07.2012
comment
Да, это тоже привлекло мое внимание. Копать глубже :) - person Sergio Tulentsev; 06.07.2012
comment
Я просто предположил, что это означает, что более старые версии компилятора MS C рассматривали два NaN как равные... - person Michael Kohl; 06.07.2012
comment
@MichaelKohl: я пытался найти, где находится этот NaN, не равный логике NaN. Кажется, это в компиляторе C :) - person Sergio Tulentsev; 06.07.2012
comment
Это определено в IEEE 754 AFAIK. Кстати: попробуйте 0 * 0/0.0, 0 ** 0/0.0 и 0/0.0 ** 0 в IRB, если вы ищете сегодня что-нибудь интересное для чтения :-) - person Michael Kohl; 06.07.2012
comment
@MichaelKohl: может быть, завтра. Сейчас переходим в спящий режим. - person Sergio Tulentsev; 06.07.2012
comment
Да, это действительно звучит как хорошая логика для меня. Но иметь объект в левой части оператора равенства, который имеет то же имя, object_id и значение, что и объект в правой части, и получить ложный результат, было для меня настоящим сюрпризом. Думаю, логика не всегда так логична. :) - person milan-j; 06.07.2012

Сделайте точно такой же код в Javascript:

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

У него будет точно такое же поведение. И такое поведение ожидаемо. Два NaN не равны между собой. Бесконечность — Бесконечность — это не то же самое, что [Одно огромное число] — [Одно и то же огромное число], это два разных огромных числа. Результат этой операции никому не известен. Итак, это NaN.

Итак, это не ошибка. Это математически правдоподобно.

Кроме того, попробуйте

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

NaN никогда не будет равен чему-либо.

person YuriAlbuquerque    schedule 05.07.2012
comment
Я просто подумал о чем-то... Бесконечность равна Бесконечности в Ruby, и если язык говорит, что это два одинаковых огромных числа, то Бесконечность-Бесконечность должна быть равна другой Бесконечности-Бесконечности, верно? :) Итак, новый вопрос: почему Бесконечность == Бесконечность, если она представляет собой два разных огромных числа, как вы говорите? - person milan-j; 06.07.2012
comment
Бесконечность минус бесконечность математически не определена. Это не число как таковое, поэтому предположение, что оно следует числовым свойствам, является ошибкой: Что касается равенства, я предполагаю, что Ruby просто сравнивает два указателя на равенство. - person Michael Kohl; 06.07.2012
comment
@j-milan хорошо, упрощаю, см. здесь: mathsisfun.com/numbers/infinity.html — есть куча математических операций, которые вы можете выполнять с Infinity, и все они математически определены. ∞ = ∞ потому что x + ∞ = ∞. Если x = 0, то ∞ = ∞. - person YuriAlbuquerque; 06.07.2012