Почему NaN не равно NaN? [дубликат]


этот вопрос уже есть ответ здесь:

соответствующий стандарт IEEE определяет числовую константу NaN (не число) и предписывает, что NaN должен сравниваться как не равный самому себе. Почему?

все языки, с которыми я знаком, реализуют это правило. Но это часто вызывает значительные проблемы, например неожиданное поведение, когда NaN хранится в контейнере, когда NaN находится в сортируемых данных и т. д. Не говоря уже о том, что подавляющее большинство программистов ожидают, что любой объект будет равен самому себе (прежде чем они узнают о NaN), поэтому удивление их добавляет ошибок и путаницы.

стандарты IEEE хорошо продуманы, поэтому я уверен, что есть веская причина, почему сравнение NaN как равно будет плохо. Я просто не могу понять, что это такое.

6 89

6 ответов:

принятый ответ 100% без вопроса неправильно. Не наполовину неправильно или даже немного неправильно. Я боюсь, что эта проблема будет путать и вводить в заблуждение программистов в течение длительного времени, когда этот вопрос всплывает в поисках.

NaN предназначен для распространения через все вычисления, заражая их, как вирус, поэтому, если где-то в ваших глубоких, сложных вычислениях вы наткнулись на NaN, вы не выдаете, казалось бы, разумный ответ. В противном случае по идентичности НАН/НАН должно быть равно 1, наряду со всеми другими последствиями, такими как (NaN/NaN)==1, (NaN*1)==NaN и т. д. Если вы представите, что ваши расчеты где-то ошиблись (округление привело к нулевому знаменателю, дав NaN) и т. д., Тогда вы можете получить дико неправильные (или хуже: тонко неправильные) результаты ваших расчетов без очевидного индикатора относительно того, почему.

есть также действительно веские причины для NaNs в расчетах при зондировании значения математической функции; один из приведенных примеров в связанном документе находится нахождение нулей () функции f (). Вполне возможно, что в процессе зондирования функции с угаданными значениями вы будете зондировать тот, где функция f() не дает никакого разумного результата. Это позволяет нулям () видеть NaN и продолжать свою работу.

альтернативой NaN является запуск исключения, как только обнаруживается незаконная операция (также называемая сигналом или ловушкой). Помимо огромных штрафов за производительность, с которыми вы можете столкнуться, на в то время не было никакой гарантии, что процессоры будут поддерживать его в аппаратном обеспечении или ОС/язык будет поддерживать его в программном обеспечении; каждый был своей собственной уникальной снежинкой в обработке с плавающей запятой. IEEE решил явно обрабатывать его в программном обеспечении в качестве значений NaN, чтобы он был переносим через любую ОС или язык программирования. Правильные алгоритмы с плавающей запятой обычно корректны во всех реализациях с плавающей запятой, будь то узел.js или COBOL (hah).

In теория, вам не нужно устанавливать определенные директивы #pragma, устанавливать сумасшедшие флаги компилятора, ловить правильные исключения или устанавливать специальные обработчики сигналов, чтобы сделать то, что кажется идентичным алгоритмом, действительно работает правильно. К сожалению, некоторые разработчики языков и составители компиляторов были действительно заняты отменой этой функции в меру своих возможностей.

пожалуйста, прочитайте некоторые сведения об истории IEEE 754 с плавающей запятой. И это ответ на аналогичный вопрос где член комитета ответил:каково обоснование для всех сравнений, возвращающих false для значений IEEE754 NaN?

"интервью со стариком с плавающей точкой"

"история формата IEEE с плавающей запятой"

что каждый ученый должен знать про арифметику с плавающей запятой

Ну log(-1) дает NaN и acos(2) дает NaN. Значит ли это, что log(-1) == acos(2)? Очевидно, что нет. Поэтому имеет смысл, что NaN не равно самому себе.

возвращаясь к этому почти два года спустя, вот функция сравнения "NaN-safe":

function compare(a,b) {
    return a == b || (isNaN(a) && isNaN(b));
}

мой оригинальный ответ (от 4 лет назад) критикует решение с современной точки зрения, не понимая контекста, в котором было принято решение. Как таковой, он не отвечает на вопрос.

правильный ответ здесь:

NaN != NaN возникла из двух прагматических соображений:

[...] Не было никакого isnan( ) предикат в то время, когда NaN был формализован в 8087 арифметика; необходимо было предоставить программистам удобные и эффективные средства обнаружения значений NaN, которые не зависели от языков программирования, предоставляющих что-то вроде isnan( ) что может занять долгие годы

у этого подхода был один недостаток: он делал NaN менее полезным во многих ситуациях, не связанных с численными вычислениями. Например, гораздо позже, когда люди захотели использовать NaN чтобы представить отсутствующие значения и поместить их в контейнеры на основе хэша, они не могли этого сделать.

если бы комитет предвидел будущие случаи использования и считал их достаточно важными, они могли бы пойти на более подробные !(x<x & x>x) вместо x!=x испытание NaN. Однако их фокус был более прагматичным и узким: обеспечение наилучшего решения для числовых вычислений, и поэтому они не видели проблем с их подходом.

===

оригинальный ответ:

мне очень жаль, но я ценю мысль, что пошел в топ-голосовал ответ, я с ним не согласен. NaN не означает "неопределенный" - см. http://www.cs.berkeley.edu/~wkahan/ieee754status / IEEE754.PDF, Страница 7 (поиск слова "неопределенный"). Как подтверждает этот документ, NaN-это четко определенная концепция.

кроме того, подход IEEE должен был следовать регулярным математическим правилам как можно больше, а когда они не могли, следовать правилу "наименьшего удивления" - см. https://stackoverflow.com/a/1573715/336527. любой математический объект равен самому себе, поэтому правила математики подразумевают, что NaN == NaN должно быть истинным. Я не вижу никакой веской и весомой причины отклоняться от такого главного математического принципа (не говоря уже о менее важных правилах трихотомии сравнения и т. д.).).

в результате мой вывод таков.

члены комитета IEEE не продумали это очень четко, и сделать ошибку. Поскольку очень немногие люди понимали подход комитета IEEE или заботились о том, что именно стандарт говорит о NaN (а именно: обработка большинства компиляторов NaN нарушает стандарт IEEE в любом случае), никто не поднял тревогу. Следовательно, эта ошибка теперь встроена в стандарт. Это вряд ли будет исправлено, так как такое исправление сломает много существующего кода.

Edit:вот один пост очень содержательное обсуждение. Примечание: чтобы получить непредвзятое представление вы нужно прочитать весь поток, так как Guido придерживается другого мнения, чем некоторые другие разработчики ядра. Однако Гвидо лично не интересуется этой темой и во многом следует рекомендации Тима Питерса. Если у кого-то есть аргументы Тима Питерса в пользу NaN != NaN, пожалуйста, добавьте их в комментарии; у них есть хороший шанс изменить мое мнение.

хорошее свойство: если x == x возвращает false, то x и NaN.

(можно использовать это свойство, чтобы проверить, если x и NaN или нет.)

попробуйте это:

var a = 'asdf';
var b = null;

var intA = parseInt(a);
var intB = parseInt(b);

console.log(intA); //logs NaN
console.log(intB); //logs NaN
console.log(intA==intB);// logs false

Если intA = = intB были истинны, это может привести вас к выводу, что a==b, что явно не так.

другой способ взглянуть на это заключается в том, что Нан просто дает вам информацию о том, что что-то не так, а не то, что это такое. Например, если я скажу "яблоко-не горилла" и "апельсин-не горилла", вы сделаете вывод, что "яблоко" == "апельсин"?

на самом деле, есть понятие в математике, известное как "единство" значений. Эти значения являются расширениями, которые тщательно сконструированы для согласования внешних проблем в системе. Например, вы можете думать о кольце на Бесконечности в комплексной плоскости как о точке или наборе точек, и некоторые ранее претенциозные проблемы уходят. Есть и другие примеры этого в отношении мощности множеств, где вы можете продемонстрировать, что вы можете выбрать структуру континуума бесконечностей так пока |P (A) | > | A / и ничего не ломается.

отказ от ответственности: я работаю только с моей смутной памятью о моих некоторых интересных предостережениях во время моих математических исследований. Я прошу прощения, если я сделал печальную работу по представлению концепций, о которых я упоминал выше.

Если вы хотите верить, что NaN является одиночным значением, то вы, вероятно, будете недовольны некоторыми результатами, такими как оператор равенства, не работающий так, как вы ожидаете/хотите. Однако, если вы решите поверить, что Нан больше из континуума "плохости", представленного одиночным заполнителем, тогда вы совершенно довольны поведением оператора равенства. Другими словами, вы теряете из виду рыбу, которую вы поймали в море, но вы ловите другую, которая выглядит так же, но так же пахнет.