Чому Double.NaN == Double.NaN повертає помилку?


155

Я тільки вивчав питання щодо OCPJP і знайшов цей дивний код:

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 є статичним остаточним, порівняння з == має повернути істину. +1 для запитання.
Стефан

2
Те саме стосується python:In [1]: NaN==NaN Out[1]: False
tdc

58
Те саме стосується всіх мов, які правильно відповідають стандарту IEEE 754.
zzzzBov

4
Інтуїція: "Привіт" - це не число, правда (булева) також не число. NaN! = NaN з тієї ж причини "Привіт"! = Вірно
Кевін

3
@Stephan: Порівняння з Double.NaN==Double.NaNдійсно повинно повернути істину, якби вони Double.NaNбули типу java.lang.Double. Однак його тип є примітивним double, а правила оператора doubleзастосовуються (які вимагають цієї нерівності для відповідності IEEE 754, як пояснено у відповідях).
sleske

Відповіді:


139

NaN означає "Не число".

Специфікація мови Java (JLS) Третього видання говорить :

Операція, що переповнює, створює підписану нескінченність, операція, що перетікає, виробляє денормалізоване значення або підписаний нуль, а операція, яка не має математично визначеного результату, виробляє NaN. Усі числові операції з NaN як операндом дають NaN як результат. Як уже було описано, NaN є не упорядкованим, тому чисельна операція порівняння, що включає один або два повернення NaN , falseі будь-яке !=порівняння, що включає повернення NaN true, включаючи, x!=xколи xце NaN.


4
@nibot: В основному це правда . Будь-яке порівняння з IEEE-відповідним поплавцем дасть результат false. Тож цей стандарт відрізняється від Java тим, що цього вимагає IEEE (NAN != NAN) == false.
Дрю Дорман

2
Відкриваючи цю скриньку Пандори - де ви бачите, що "IEEE вимагає цього (NAN! = NAN) == false"?
Наглядач

62

NaN за визначенням не дорівнює жодному числу, включаючи NaN. Це частина стандарту IEEE 754 та реалізована ЦП / ФПУ. Це не те, що JVM повинен додавати будь-яку логіку для підтримки.

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

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

Java ставиться до всіх NaN як до тихого NaN.


1
Чи реалізовано це процесором, або він є провідним в JVM, як згадує богем?
Naweed Chougle

3
JVM повинен викликати те, що реалізує це правильно. На ПК процесор виконує всю роботу як таку. На машині без цієї підтримки JVM має її реалізувати. (Я не знаю жодної такої машини)
Пітер Лорі

Ще в той час, коли 8087 був варіантом, бібліотека C містила емулятор FP. Такі програми, як JVM, не повинні були б турбуватися про це в будь-якому випадку.
Маркіз Лорн

49

Чому така логіка

NaNзасоби Not a Number. Що не число? Що завгодно. Ви можете мати що завгодно в одній стороні і будь-що в іншій стороні, тому нічого не гарантує, що обидва рівні. NaNрозраховується з Double.longBitsToDouble(0x7ff8000000000000L)та, як ви бачите в документації longBitsToDouble:

Якщо аргументом є будь-яке значення в діапазоні 0x7ff0000000000001Lчерез 0x7fffffffffffffffLабо в діапазоні 0xfff0000000000001Lнаскрізь 0xffffffffffffffffL, то результат a 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);

Чи знаєте ви про будь-який випадок, коли NaN != NaNхибність ускладнювала б програми, ніж NaN != NaNістина? Я знаю, що IEEE прийняв рішення ще давно, але з практичної точки зору я ніколи не бачив випадків, коли це було б корисно. Якщо передбачається, що операція буде тривати, поки послідовні ітерації не дадуть однакового результату, то при двох повторних ітераціях вихід NaN був би "природним чином" виявлений як вихідний стан, якби не така поведінка.
supercat

@supercat Як можна сказати, що два випадкових нечисельних числа є природними рівними? Або скажімо, примітивно рівний? Розгляньте NaN як екземпляр, а не щось примітивне. Кожен різний аномальний результат є різним екземпляром чогось дивного, і навіть якщо обидва повинні представляти однакове, використовуючи == для різних примірників, потрібно повернути помилкове. З іншого боку, при використанні рівних з ним можна поводитися належним чином, як ви плануєте. [ docs.oracle.com/javase/7/docs/api/java/lang/…
falsarella

@falsarella: Проблема полягає не в тому, чи слід вважати два випадкові числа «однозначно рівними», а, скоріше, у яких випадках корисно будь-яке число порівняти як «однозначно неоднакове». Якщо хтось намагається обчислити межу f(f(f...f(x))), і знайде a y=f[n](x)для деякого nтакого, що результат f(y)не відрізняється від y, то yвін буде невідрізний від результату будь-якого більш глибоко вкладеного f(f(f(...f(y))). Навіть якби хтось хотів NaN==NaNбути помилковим, те, що Nan!=Nan також було неправдивим, було б менш "дивно", ніж x!=xсправжнє для деяких x.
supercat

1
@falsarella: Я вважаю, що Double.NaNце не так Double, але double, тому питання пов'язане з поведінкою double. Хоча існують функції, які можуть перевірити співвідношення еквівалентності із doubleзначеннями, я знаю єдину переконливу відповідь "чому" (що є частиною оригінального питання) "тому, що деякі люди в IEEE не думали, що тестування рівності повинно визначити відношення еквівалентності ". До речі, чи є якийсь стислий ідіоматичний спосіб перевірити xта yна еквівалентність використовувати лише примітивні оператори? Усі рецепти, про які я знаю, досить незграбні.
supercat

1
найкраща і проста відповідь. Дякую
Тарун Нагпал

16

Це може бути не прямою відповіддю на питання. Але якщо ви хочете перевірити, чи щось вам дорівнює, Double.NaNскористайтеся цим:

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

Це повернеться true


6

Javadoc для Double.NaN говорить все це:

Постійна, що містить значення типу «Не-число» (NaN) double. Це еквівалентно величині, поверненій Double.longBitsToDouble(0x7ff8000000000000L).

Цікаво, що джерело для цього Doubleвизначає NaN:

public static final double NaN = 0.0d / 0.0;

Особлива поведінка, яку ви описуєте, є провідною в JVM.


5
Це жорсткий провід у JVM, чи його реалізує процесор, як згадує Пітер?
Naweed Chougle

4

відповідно до стандарту IEEE для арифметики з плаваючою комою для чисел з подвійною точністю,

Стандартне подання з плаваючою крапкою IEEE з подвійною точністю вимагає 64-бітного слова, яке може бути представлене як пронумеровано від 0 до 63, зліва направо

введіть тут опис зображення де,

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"

3

NaN - особливе значення, яке позначає "не число"; це результат певних недійсних арифметичних операцій, таких як sqrt(-1), і має (іноді дратує) властивість, яка NaN != NaN.


2

Не число представляє результат операцій, результат яких не можна представити числом. Найвідоміша операція - 0/0, результат якої не відомий.

З цієї причини NaN не дорівнює жодному (включаючи інші значення не числа). Для отримання додаткової інформації перегляньте сторінку wikipedia: http://en.wikipedia.org/wiki/NaN


-1: Це не означає результат 0/0. 0/0завжди NaN, але NaN може бути результатом інших операцій - таких як 2+NaN: an operation that has no mathematically definite result produces NaNвідповідно до відповіді @AdrianMitev
ANeves

Дійсно, NaN означає «Не число», і це результат усіх операцій, які в результаті мають невизначене або непредставлене значення. Найвідоміша і найпоширеніша операція - 0/0, але, очевидно, є багато інших операцій, які мають той же результат. Я погоджуюся з тим, що мою відповідь можна було б покращити, але я не погоджуюся з питаннями -1 ... Я просто перевірив, що також wikipedia використовує операції 0/0 як перший приклад роботи з результатом NaN ( en.wikipedia.org/wiki/ NaN ).
Маттео

Крім того, це є у джерелі Java для Double: public static final double NaN = 0.0d / 0.0;
Гійом

1
@Matteo +0, тепер, коли помилкового твердження вже немає. І мої -1 або +1 не для вас, щоб погодитися чи не погодитися; але добре залишити коментар з позначкою -1, щоб автор міг зрозуміти, чому його відповідь вважають непотрібною - і змінити її, якщо він цього захоче.
ANeves

@Guillaume, якщо цей коментар був призначений для мене, будь ласка, перефразуйте його: я його не розумію.
ANeves

0

За цим посиланням , в ньому виникають різні ситуації і важко запам’ятати. Ось як я їх пам’ятаю і розрізняю. NaNозначає "математично невизначений", наприклад: "результат 0, поділений на 0, не визначений", і тому що він не визначений, тому "порівняння, пов'язане з невизначеним, звичайно, не визначене". Крім того, він більше схожий на математичні приміщення. З іншого боку, і позитивна, і негативна нескінченність є заздалегідь визначеною і остаточною, наприклад, "позитивна або негативна нескінченна велика чітко визначена математично".

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.