Float.NaN == Float.NaN

Почему это сравнение дает мне «ложь»? Я посмотрел на источник, и Float.NaN определяется как

/** 
 * A constant holding a Not-a-Number (NaN) value of type
 * <code>float</code>.  It is equivalent to the value returned by
 * <code>Float.intBitsToFloat(0x7fc00000)</code>.
 */
public static final float NaN = 0.0f / 0.0f;

РЕДАКТИРОВАТЬ: удивительно, если я сделаю это:

System.out.println("FC " + (Float.compare(Float.NaN, Float.NaN)));

это дает мне 0. Итак, Float.compare() действительно думает, что NaN равно самому себе!


person shrini1000    schedule 18.02.2012    source источник


Ответы (3)


Поскольку Java реализует стандарт с плавающей запятой IEEE-754, который гарантирует, что любое сравнение с NaN вернет false (кроме !=, которое возвращает true)

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

def isNan(val):
     return val != val
person Voo    schedule 18.02.2012
comment
За исключением != сравнений, которые возвращают true. - person Daniel Fischer; 18.02.2012
comment
Вы действительно можете проверить NaN таким образом! если x==x ложно, то x равно NaN. - person David Heffernan; 18.02.2012
comment
Вы также можете использовать статический Float.isNaN(float) (и у Double тоже есть его версия). - person yshavit; 16.05.2012
comment
Интересно, какое было обоснование? Я понимаю причину рассмотрения NaN как ни больше, ни равно, ни меньше, чем что-либо еще, но наиболее разумным значением для x==y было бы то, что x неотличим от y, и если и x, и y оказываются NaN, это утверждение будет истинный. Я не могу придумать разумного вопроса, на который можно было бы ответить с помощью правил равенства, установленных IEEE. - person supercat; 15.08.2013
comment
@supercat Я считаю, что причина того, что !(NaN == Nan) заключается в том, что NaN можно использовать для представления очень многих вещей. Например, (3.0/0.0) == (1.0/0.0)? Трудно однозначно сказать «да», поэтому я думаю, что именно поэтому они не сравниваются как равные. - person Upgrayedd; 11.04.2014
comment
@Upgrayedd Плохой пример, потому что 3.0/0 - это не NaN, а бесконечность (и да, это будет равнозначно другим бесконечностям). С математической точки зрения это имеет смысл: 0/0 не равно ни бесконечности, ни 0, результат неопределен (иначе это нарушило бы существующее свойство чисел, независимо от того, какой результат мы выбираем). А у неопределенных результатов есть особые правила, иначе вы столкнетесь с парадоксами. - person Voo; 11.04.2014
comment
@Voo: значения с плавающей запятой не могут подчиняться всем аксиомам арифметики; Однако я бы сказал, что наиболее важной аксиомой для любого типа значения является то, что значения должны определять классы эквивалентности. Даже если эти классы эквивалентности не полностью соблюдают все остальные правила арифметики (например, x==y не означает, что 1/x == 1/y), они должны по крайней мере соблюдать аксиомы, связанные с самими классами эквивалентности. (например, x==y и y==z подразумевает x==z). Если бы я разрабатывал язык, я бы включил оператор равенства, который всегда реализовывал бы отношение эквивалентности... - person supercat; 12.04.2014
comment
... для любой комбинации операндов, которые будут компилироваться, даже если есть также оператор равенства с плавающей запятой, который реализует равенство IEEE. Смешанные сравнения, такие как someLong==someDouble, будут разрешены только в тех случаях, когда язык может обеспечить поведение эквивалентности (путем проверки того, что оба (double)someLong==someDouble и someLong==(long)someDouble верны). Операторы равенства, не представляющие отношения эквивалентности, приводят к запутанной семантике. - person supercat; 12.04.2014

Используйте Float.isNaN для проверки значений NaN.

person pencil    schedule 18.02.2012
comment
@Rolfツ Добавить нечего, все довольно просто. - person flawyte; 09.01.2015

Все, что мне нужно сказать, это: Википедия о NaN.

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

s111 1111 1xxx xxxx xxxx xxxx xxxx xxxx

S — это знак (отрицательный или положительный), 1 — показатель степени, а x — полезная нагрузка.

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

Другое дело, что в стандарте у них есть сигнализация и вполне NaN. Сигнальный NaN (sNaN) означает NaN, который должен вызывать реакцию как исключение. Его следует использовать, чтобы сказать вслух, что у вас есть проблема в вашем уравнении. Тихий NaN (qNaN) — это NaN, который передается молча.

sNaN, который создал сигнал, преобразуется в qNaN, чтобы больше не генерировать никаких сигналов в последующих операциях. Помните, что какая-то система определяет i^0 = 1 как константу, для которой верно NaN^0 = 1. Так что бывают случаи, когда люди рассчитывают с NaN.

Итак, в конце концов, я бы выбрал это: qNaN != sNaN, но это внутреннее и невидимое для пользователя (вы не можете это проверить). Смешайте платеж и знак (да, вы можете иметь отрицательный и положительный NaN), и мне кажется, что всегда возвращается NaN != NaN выглядит гораздо более мудрым выбором, который я, наконец, научился ценить -> Я никогда не буду жаловаться или удивляться еще раз о неравенстве NaN. Слава людям, которые были настолько вдумчивы, что дали нам такой хороший стандарт!

Между прочим: Java использует положительный NaN с полезной нагрузкой 0 (все x равны нулю).

person Martin Kersten    schedule 21.09.2015
comment
Какое отношение комплексные числа имеют к полезной нагрузке? - person Mark Dickinson; 23.09.2015
comment
@MarkDickinson, страница в Википедии предлагает использовать sNaN в качестве заполнителя для более сложного объекта, такого как […] комплексное число - person Janne Aukia; 14.11.2018