2012-01-11 6 views
136

Я просто изучая OCPJP вопросы, и я нашел этот странный код:Почему Double.NaN == Double.NaN возвращает false?

public static void main(String a[]) { 
    System.out.println(Double.NaN==Double.NaN); 
    System.out.println(Double.NaN!=Double.NaN); 
} 

Когда я запускал код, я получил:

false 
true 

Как выход false, когда мы сравниваем две вещи которые выглядят одинаково друг с другом? Что означает NaN?

+8

Это действительно странно. Поскольку Double.NaN является статическим окончательным, сравнение с == должно возвращать true. +1 для вопроса. – Stephan

+2

То же самое верно в python: 'In [1]: NaN == NaN Out [1]: False' – tdc

+53

То же самое верно на всех языках, которые правильно следуют стандарту IEEE 754. – zzzzBov

ответ

126

NaN означает «Не номер».

Java Language Specification (JLS) Third Edition says:

Операция, которая разливается производит подписанную бесконечность, операция, которая производит недостаточный денормализованного значение или ноль, подписанный и операцию, которая не имеет математически определенный результата производит NaN. В результате числовые операции с NaN в качестве операнда дают NaN. Как уже было описано, NaN неупорядочен, поэтому операция числового сравнения с одним или двумя NaN возвращает false и любое сравнение != с использованием NaN возвращает true, включая x!=x, когда x является NaN.

+47

Это не просто Java; он находится в стандарте с плавающей точкой. – nibot

+4

@nibot: ** В основном верно **. Любое сравнение с поплавком, соответствующим IEEE, приведет к ошибке «false». Таким образом, стандарт отличается от Java тем, что IEEE требует, чтобы '(NAN! = NAN) == false'. –

+2

Открытие этого окна Пандоры - где вы видите, что «IEEE требует, чтобы (NAN! = NAN) == false»? – Supervisor

3

NaN - особое значение, которое обозначает «не число»; это результат определенных недействительных арифметических операций, таких как sqrt(-1), и имеет (иногда раздражающее) свойство, которое NaN != NaN.

6

javadoc for Double.NaN говорит все это:

Константа держит значение не является числом (NaN) типа double. Это эквивалентно значению, возвращаемому Double.longBitsToDouble(0x7ff8000000000000L).

Интересно, что источник Double определяет NaN таким образом:

public static final double NaN = 0.0d/0.0; 

Особое поведение вы описываете зашитыми в JVM.

+5

Является ли он жестко подключен к JVM, или он реализован процессором, как упоминает Питер? – Enthusiast

2

Не число представляет собой результат операций, результат которых не представляется числом с номером. Самая известная операция - 0/0, результат которой неизвестен.

По этой причине NaN не соответствует чему-либо (включая другие значения, отличные от числа). Для получения дополнительной информации, просто проверить страницу википедии: http://en.wikipedia.org/wiki/NaN

+0

-1: Это ** не ** представляет результат '0/0'. '0/0' всегда NaN, но NaN может быть результатом других операций, таких как' 2 + NaN': 'операция, которая не имеет математически определенного результата, производит NaN', согласно ответу @AdrianMitev – ANeves

+0

Действительно, NaN означает «Не число», и это результат всех операций, которые в результате дают неопределенное или непредставимое значение. Самая известная и общая операция - 0/0, но, очевидно, есть много других операций, которые имеют тот же результат. Я согласен с тем, что мой ответ может быть улучшен, но я не согласен с -1 ... Я только что проверил, что также в википедии используются операции 0/0 в качестве первого примера операции с результатом NaN (http: //en.wikipedia. орг/вики/NaN). – Matteo

+0

Кроме того, это в источнике Java для Double: public static final double NaN = 0.0d/0.0; – Guillaume

44

Почему эта логика

NaN означает Not a Number. Что не число? Что-нибудь. Вы можете иметь что-нибудь в одной стороне и что-нибудь в другой стороне, поэтому ничто не гарантирует, что оба они равны.NaN рассчитывается с Double.longBitsToDouble(0x7ff8000000000000L) и, как вы можете увидеть в документации longBitsToDouble:

Если аргумент ссылается на любое значение в диапазоне 0x7ff0000000000001L через 0x7fffffffffffffffL или в диапазоне 0xfff0000000000001L через 0xffffffffffffffffL, результатом является NaN.

Кроме того, NaN логически обрабатывается внутри API.


Документация

/** 
* A constant holding a Not-a-Number (NaN) value of type 
* {@code double}. It is equivalent to the value returned by 
* {@code Double.longBitsToDouble(0x7ff8000000000000L)}. 
*/ 
public static final double NaN = 0.0d/0.0; 

Кстати, NaNявляется протестирован в качестве образца кода:

/** 
* Returns {@code true} if the specified number is a 
* Not-a-Number (NaN) value, {@code false} otherwise. 
* 
* @param v the value to be tested. 
* @return {@code true} if the value of the argument is NaN; 
*   {@code false} otherwise. 
*/ 
static public boolean isNaN(double v) { 
    return (v != v); 
} 

Решение

Что вы можете сделать, это использовать compare/compareTo:

Double.NaN считается этим методом равным себе и больше, чем все остальные double значений (в том числе Double.POSITIVE_INFINITY).

Double.compare(Double.NaN, Double.NaN); 
Double.NaN.compareTo(Double.NaN); 

Или equals:

Если this и argument оба представляют Double.NaN, то метод equals возвращает true, даже если Double.NaN==Double.NaN имеет значение false.

Double.NaN.equals(Double.NaN); 
+0

Знаете ли вы, что любой случай, когда «NaN! = NaN» будет ложным, сделает программы более сложными, чем «NaN! = NaN» будет истинным? Я знаю, что IEEE принял решение давным-давно, но с практической точки зрения, я никогда не видел случаев, когда это полезно. Если предполагается, что операция будет выполняться до тех пор, пока последовательные итерации не дадут тот же результат, имея две последовательные итерации, выход NaN будет «естественно» обнаружен как условие выхода, если бы не такое поведение. – supercat

+0

@supercat Как вы можете сказать, что два случайных числа не равны, естественно? Или сказать, примитивно равны? Подумайте о NaN как о экземпляре, а не о чем-то примитивном. Каждый другой ненормальный результат - это другой экземпляр чего-то странного, и даже если оба должны представлять одно и то же, использование == для разных экземпляров должно возвращать false. С другой стороны, при использовании equals его можно обрабатывать правильно, как вы планируете. [http://docs.oracle.com/javase/7/docs/api/java/lang/Double.html#equals(java.lang.Object)] – falsarella

+0

@falsarella: проблема заключается не в том, должны ли два случайных числа считаются «определенно равными», а скорее в каких случаях полезно сравнивать любое число как «определенно неравное» с самим собой. Если вы пытаетесь вычислить предел 'f (f (f ... f (x))), и вы найдете' y = f [n] (x) 'для некоторого' n', так что результат из 'f (y)' неотличим от 'y', тогда' y' будет неотличим от результата более глубоко-вложенного 'f (f (f (... f (y))). Даже если бы кто-то хотел, чтобы NaN == NaN был ложным, то значение 'Nan! = Nan' * также * было бы ложным, было бы менее« неожиданным », чем если бы xx = x было истинным для некоторого x. – supercat

59

NaN по определению не равно любому числу, включая NaN. Это часть стандарта IEEE 754 и реализуется CPU/FPU. Это не то, что JVM должно добавить любую логику для поддержки.

http://en.wikipedia.org/wiki/NaN

Сравнение с NaN всегда возвращает неупорядоченный результат даже при сравнении с самим собой. ... Предикаты равенства и неравенства не сигнализируют, так что x = x возвращение false может быть использовано для проверки, является ли x тихим NaN.

Java рассматривает все NaN как спокойные NaN.

+1

Реализовано ли оно процессором, или оно жестко связано с JVM, как упоминает Богем? – Enthusiast

+3

JVM должен вызывать все, что будет правильно реализовывать. На ПК процессор выполняет всю работу как таковую. На машине без этой поддержки JVM должна ее реализовать. (Я не знаю никакой такой машины) –

+0

В тот день, когда 8087 был опцией, библиотека C содержала эмулятор FP. Такие программы, как JVM, не должны беспокоиться об этом в любом случае. – EJP

3

в соответствии, The IEEE standard for floating point arithmetic для чисел двойной точности,

Стандарт IEEE двойной точности с плавающей точкой стандартное представление требуется 64 битового слова, которые могут быть представлены в виде пронумерованных от 0 до 63, слева направо

enter image description here где

S: Sign – 1 bit 
E: Exponent – 11 bits 
F: Fraction – 52 bits 

Если E=2047 (все E являются 1) и F отличен от нуля, то V=NaN ("Не число")

Это означает,

Если все E биты 1, и если есть любой ненулевой бит в F, затем номер NaN.

поэтому, в частности, все следующие числа NaN,

0 11111111 0000000000000000010000000000000000000000000000000000 = NaN 
1 11111111 0000010000000000010001000000000000001000000000000000 = NaN 
1 11111111 0000010000011000010001000000000000001000000000000000 = NaN 

В частности, вы не можете проверить

if (x == Double.NaN) 

проверить, равен ли конкретный результат Double.NaN, потому что все «не число «Значения считаются отличными. Тем не менее, вы можете использовать метод Double.isNaN:

if (Double.isNaN(x)) // check whether x is "not a number" 
10

Оно не может быть прямым ответом на вопрос. Но если вы хотите, чтобы проверить, если что-то равно Double.NaN вы должны использовать это:

double d = Double.NaN 
Double.isNaN(d); 

Это вернет true

0

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