Вопрос по – Почему NaN не равен NaN? [Дубликат]

97

This question already has an answer here:

What is the rationale for all comparisons returning false for IEEE754 NaN values? 13 answers

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

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

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

Стандарты IEEE были разработаны инженерами, а не программистами, поставщиками компьютеров или авторами математических библиотек, для которых правило NaN является катастрофой. Jim Balter

Ваш Ответ

6   ответов
135

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

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

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

Альтернатива NaN состоит в том, чтобы вызвать исключение, как только встречается недопустимая операция (также называемая сигналом или ловушкой). Помимо огромных потерь производительности, с которыми вы могли столкнуться, в то время не было никакой гарантии, что процессоры будут поддерживать его аппаратно или ОС / язык будет поддерживать его программно; у каждого была своя уникальная снежинка при работе с плавающей точкой.IEEE decided to explicitly handle it in software as the NaN values so it would be portable across any OS or programming language. Correct floating point algorithms are generally correct across all floating point implementationsбудь то node.js или COBOL (хах).

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

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

& quot; Интервью со стариком с плавающей точкой & quot;

& quot; История формата IEEE с плавающей запятой & quot;

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

Error: User Rate Limit Exceeded max
Error: User Rate Limit Exceededisnan()Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded max
Error: User Rate Limit Exceeded
Error: User Rate Limit ExceededNaN + 1 != 0Error: User Rate Limit ExceededNaN * 1 > 0Error: User Rate Limit ExceededTrueError: User Rate Limit ExceededFalseError: User Rate Limit ExceededNaNError: User Rate Limit ExceededNaN/NaN == 1Error: User Rate Limit ExceededTrueError: User Rate Limit Exceeded max
7

x == x возвращает ложь, тогдаx являетсяNaN.

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

Error: User Rate Limit Exceeded
Error: User Rate Limit ExceededalsoError: User Rate Limit ExceededaError: User Rate Limit ExceededbError: User Rate Limit Exceeded!(a != b).
2

известное как & # x201C; unity & # x201D; ценности. Эти значения являются расширениями, которые тщательно сконструированы для устранения внешних проблем в системе. Например, вы можете думать о кольце на бесконечности в сложной плоскости как о точке или наборе точек, и некоторые ранее претенциозные проблемы исчезают. Есть и другие примеры этого в отношении количества множеств, где вы можете продемонстрировать, что вы можете выбрать структуру континуума бесконечностей, пока | P (A) | & GT; | | и ничего не ломается.

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: Я работаю только со смутным воспоминанием о некоторых интересных предостережениях во время обучения математике. Я приношу свои извинения, если сделал ужасную работу по представлению концепций, на которые я ссылался выше.

Если вы хотите верить, что NaN является единственным значением, то вы, вероятно, будете недовольны некоторыми результатами, такими как оператор равенства, который работает не так, как вы ожидаете / хотите. Однако, если вы решите верить, что NaN - это скорее континуум & # x201C; badness & # x201D; представленный одиночным заполнителем, тогда вы совершенно довольны поведением оператора равенства. Другими словами, вы теряете из виду рыбу, которую вы поймали в море, но вы ловите другую, которая выглядит так же, но такая же вонючая.

Да, по математике можно добавитьinfinity и подобные ценности. Однако они никогда не нарушат отношения эквивалентности. Программисты & APOS; равенствоrepresents an equivalence relation in math, что по определению рефлексивно. Плохой программист может определить== это не рефлексивно, симметрично и транзитивно; К сожалению, Python не остановит его. Но когда сам Python делает== нерефлексивный, и вы даже не можете его переопределить, это полная катастрофа как с практической точки зрения (членство в контейнере), так и с точки зрения элегантности / ясности ума max
91

log(-1) даетNaN, а такжеacos(2) также даетNaN, Означает ли это, чтоlog(-1) == acos(2)? Очевидно, нет. Следовательно, совершенно логично, чтоNaN не равно себе.

Возвращаясь к этому почти два года спустя, здесь «NaN-безопасный» функция сравнения:

function compare(a,b) {
    return a == b || (isNaN(a) && isNaN(b));
}
Error: User Rate Limit Exceeded
1 + 3 = 4 а также2 + 2 = 4 , Означает ли это, что1 + 3 = 2 + 2Error: User Rate Limit Exceeded
Error: User Rate Limit Exceededlog функция иacosError: User Rate Limit Exceeded-1 будет рассматриваться как пересечение. Что интересно,Infinity == InfinityError: User Rate Limit Exceeded
Ноlog(-1) != log(-1)Error: User Rate Limit ExceededNaN равняетсяNaN ниNaN не равноNaN имеет смысл во всех случаях. Возможно, это имеет больше смысла, еслиNaN == NaNError: User Rate Limit Exceeded==Error: User Rate Limit Exceeded
Учитывая, что Inf == Inf, и учитывая, что можно так же легко утверждать, что объект должен быть равен самому себе, я подозреваю, что за выбором IEEE было какое-то другое, очень конкретное и очень сильное обоснование ... max
6

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, что явно не так.

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

Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceededa=16777216f, b=0.25, а такжеc=0.125, если тот факт, чтоa+b == a+cError: User Rate Limit Exceededb==cError: User Rate Limit ExceededindistinguishableError: User Rate Limit Exceeded
28

й точки зрения без понимания контекста, в котором было принято решение. Как таковой, он не отвечает на вопрос.

Правильный ответ даетсяВот:

NaN != NaN originated out of two pragmatic considerations:

[...] There was no isnan( ) predicate at the time that NaN was formalized in the 8087 arithmetic; it was necessary to provide programmers with a convenient and efficient means of detecting NaN values that didn’t depend on programming languages providing something like isnan( ) which could take many years

У этого подхода был один недостаток: он делал 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 должно быть True. Я не вижу никакой веской и веской причины отклоняться от такого важного математического принципа (не говоря уже о менее важных правилах трихотомии сравнения и т. Д.).

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

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

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

Error: User Rate Limit Exceededhow NaN != NaNError: User Rate Limit ExceededNaNError: User Rate Limit ExceededNaNError: User Rate Limit Exceededisnan().
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit ExceededNaNError: User Rate Limit Exceeded==Error: User Rate Limit Exceeded
Error: User Rate Limit ExceededNaN==NaNError: User Rate Limit Exceeded(a<b)Error: User Rate Limit Exceeded!(a>=b)Error: User Rate Limit Exceeded(a==b) должен обязательно равняться!(a!=b)Error: User Rate Limit ExceededNaN==NaNError: User Rate Limit ExceededNan!=NaNError: User Rate Limit Exceeded

Похожие вопросы