Что означает NaN в Java?

108

У меня есть программа, которая пытается уменьшить doubleдо желаемого числа. На выходе я получаю NaN.

Что NaNзначит на Java?

Дэвид
источник
Есть хорошее описание NaN и распространенных ошибок при использовании NaN в Java: ppkwok.blogspot.co.uk/2012/11/…
Phil
Если вы спрашиваете, "что хорошего в NaN?" на Java (или на любом другом языке) я могу дать вам пример использования, где это очень удобно: когда у меня есть двумерный массив с плавающей запятой, но мои вычисления не имеют значимого значения для некоторой части этого двумерного массива, Я заполню это значение "NaN". Это можно использовать, чтобы сигнализировать последующим пользователям о моих вычислениях (например, когда они превращаются в растровое изображение) «не обращайте внимания на значение в этот момент». Очень полезно!
Dan H
Кстати, что - точно - значит "сжать" дубль? Любопытно ...
Дэн Х

Ответы:

154

Взято с этой страницы :

«NaN» означает «не число». "Nan" создается, если операция с плавающей запятой имеет некоторые входные параметры, которые приводят к тому, что операция дает неопределенный результат. Например, 0,0, деленное на 0,0, арифметически не определено. Извлечение квадратного корня из отрицательного числа также не определено.

Кенни Деримакер
источник
16
Кроме того, NaN определено Стандартом IEEE для арифметики с плавающей точкой (IEEE 754) довольно явно, которому Java слепо следует. Чтение стандарта открывает вам глаза на множество вещей, в том числе множественные нулевые значения.
Esko
37
Кроме того, он NaNобладает интересным свойством быть единственным «числом», которое при сравнении отличается от него самого. Поэтому общее (и во многих языках только) тест , если число xявляется NaNзаключается в следующем:boolean isNaN(x){return x != x;}
quazgar
3
Ссылка в ответ мертва?
Pang
3
... "Извлечение квадратного корня из отрицательного числа не определено (в арифметике)" ... Это не так! это на самом деле, iи некоторые языки, такие как python, очень хорошо с этим справляются ... Это может быть не так в javaтебе
Рафаэль Т
5
@RafaelT Я бы сказал, что это не определено в несложной арифметике. В Java нет способа присвоить комплексное число float или double. Python динамически типизирован, поэтому в этом случае может быть просто возвращать комплексное число.
sstn
19

NaNозначает «Не число» и в основном представляет собой представление специального значения с плавающей запятой в стандарте с плавающей запятой IEE 754 . NaN обычно означает, что значение не может быть выражено действительным числом с плавающей запятой.

Преобразование приведет к получению этого значения, когда преобразуемое значение является чем-то другим, например, при преобразовании строки, которая не представляет собой число.

тыкать
источник
Как преобразовать? С parseFloat()или parseDouble? Или что-то другое?
Алонсо дель Арте,
14

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

sepp2k
источник
5

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

Смотрите здесь для более подробного объяснения этого значения.

Майк Дэниелс
источник
5

NaN означает не число. Он используется для обозначения любого значения, которое математически не определено. Как деление 0,0 на 0,0. Вы можете найти здесь дополнительную информацию: https://web.archive.org/web/20120819091816/http://www.concentric.net/~ttwang/tech/javafloat.htm

Разместите здесь свою программу, если вам нужна дополнительная помощь.

Prachi
источник
4

Значит не число. Это обычное представление невозможного числового значения во многих языках программирования.

лбедогни
источник
4

Минимальный запускаемый пример

Первое, что вам нужно знать, это то, что концепция NaN реализована непосредственно на аппаратном обеспечении ЦП.

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

Следовательно, концепция будет очень похожей для любого языка, включая Java, которая просто передает код с плавающей запятой непосредственно в ЦП.

Прежде чем продолжить, вы можете сначала прочитать следующие ответы, которые я написал:

Теперь немного о Java. Большинство интересующих функций, которых нет в основном языке, живут внутриjava.lang.Float .

Nan.java

import java.lang.Float;
import java.lang.Math;

public class Nan {
    public static void main(String[] args) {
        // Generate some NaNs.
        float nan            = Float.NaN;
        float zero_div_zero  = 0.0f / 0.0f;
        float sqrt_negative  = (float)Math.sqrt(-1.0);
        float log_negative   = (float)Math.log(-1.0);
        float inf_minus_inf  = Float.POSITIVE_INFINITY - Float.POSITIVE_INFINITY;
        float inf_times_zero = Float.POSITIVE_INFINITY * 0.0f;
        float quiet_nan1     = Float.intBitsToFloat(0x7fc00001);
        float quiet_nan2     = Float.intBitsToFloat(0x7fc00002);
        float signaling_nan1 = Float.intBitsToFloat(0x7fa00001);
        float signaling_nan2 = Float.intBitsToFloat(0x7fa00002);
        float nan_minus      = -nan;

        // Generate some infinities.
        float positive_inf   = Float.POSITIVE_INFINITY;
        float negative_inf   = Float.NEGATIVE_INFINITY;
        float one_div_zero   = 1.0f / 0.0f;
        float log_zero       = (float)Math.log(0.0);

        // Double check that they are actually NaNs.
        assert  Float.isNaN(nan);
        assert  Float.isNaN(zero_div_zero);
        assert  Float.isNaN(sqrt_negative);
        assert  Float.isNaN(inf_minus_inf);
        assert  Float.isNaN(inf_times_zero);
        assert  Float.isNaN(quiet_nan1);
        assert  Float.isNaN(quiet_nan2);
        assert  Float.isNaN(signaling_nan1);
        assert  Float.isNaN(signaling_nan2);
        assert  Float.isNaN(nan_minus);
        assert  Float.isNaN(log_negative);

        // Double check that they are infinities.
        assert  Float.isInfinite(positive_inf);
        assert  Float.isInfinite(negative_inf);
        assert !Float.isNaN(positive_inf);
        assert !Float.isNaN(negative_inf);
        assert one_div_zero == positive_inf;
        assert log_zero == negative_inf;
            // Double check infinities.

        // See what they look like.
        System.out.printf("nan            0x%08x %f\n", Float.floatToRawIntBits(nan           ), nan           );
        System.out.printf("zero_div_zero  0x%08x %f\n", Float.floatToRawIntBits(zero_div_zero ), zero_div_zero );
        System.out.printf("sqrt_negative  0x%08x %f\n", Float.floatToRawIntBits(sqrt_negative ), sqrt_negative );
        System.out.printf("log_negative   0x%08x %f\n", Float.floatToRawIntBits(log_negative  ), log_negative  );
        System.out.printf("inf_minus_inf  0x%08x %f\n", Float.floatToRawIntBits(inf_minus_inf ), inf_minus_inf );
        System.out.printf("inf_times_zero 0x%08x %f\n", Float.floatToRawIntBits(inf_times_zero), inf_times_zero);
        System.out.printf("quiet_nan1     0x%08x %f\n", Float.floatToRawIntBits(quiet_nan1    ), quiet_nan1    );
        System.out.printf("quiet_nan2     0x%08x %f\n", Float.floatToRawIntBits(quiet_nan2    ), quiet_nan2    );
        System.out.printf("signaling_nan1 0x%08x %f\n", Float.floatToRawIntBits(signaling_nan1), signaling_nan1);
        System.out.printf("signaling_nan2 0x%08x %f\n", Float.floatToRawIntBits(signaling_nan2), signaling_nan2);
        System.out.printf("nan_minus      0x%08x %f\n", Float.floatToRawIntBits(nan_minus     ), nan_minus     );
        System.out.printf("positive_inf   0x%08x %f\n", Float.floatToRawIntBits(positive_inf  ), positive_inf  );
        System.out.printf("negative_inf   0x%08x %f\n", Float.floatToRawIntBits(negative_inf  ), negative_inf  );
        System.out.printf("one_div_zero   0x%08x %f\n", Float.floatToRawIntBits(one_div_zero  ), one_div_zero  );
        System.out.printf("log_zero       0x%08x %f\n", Float.floatToRawIntBits(log_zero      ), log_zero      );

        // NaN comparisons always fail.
        // Therefore, all tests that we will do afterwards will be just isNaN.
        assert !(1.0f < nan);
        assert !(1.0f == nan);
        assert !(1.0f > nan);
        assert !(nan == nan);

        // NaN propagate through most operations.
        assert Float.isNaN(nan + 1.0f);
        assert Float.isNaN(1.0f + nan);
        assert Float.isNaN(nan + nan);
        assert Float.isNaN(nan / 1.0f);
        assert Float.isNaN(1.0f / nan);
        assert Float.isNaN((float)Math.sqrt((double)nan));
    }
}

GitHub вверх по течению .

Бежать с:

javac Nan.java && java -ea Nan

Вывод:

nan            0x7fc00000 NaN
zero_div_zero  0x7fc00000 NaN
sqrt_negative  0xffc00000 NaN
log_negative   0xffc00000 NaN
inf_minus_inf  0x7fc00000 NaN
inf_times_zero 0x7fc00000 NaN
quiet_nan1     0x7fc00001 NaN
quiet_nan2     0x7fc00002 NaN
signaling_nan1 0x7fa00001 NaN
signaling_nan2 0x7fa00002 NaN
nan_minus      0xffc00000 NaN
positive_inf   0x7f800000 Infinity
negative_inf   0xff800000 -Infinity
one_div_zero   0x7f800000 Infinity
log_zero       0xff800000 -Infinity

Итак, из этого мы узнаем несколько вещей:

  • странные операции с плавающей запятой, не дающие разумного результата, дают NaN:

    • 0.0f / 0.0f
    • sqrt(-1.0f)
    • log(-1.0f)

    создать NaN.

    В C на самом деле можно запрашивать сигналы, которые должны генерироваться при таких операциях, feenableexceptдля их обнаружения, но я не думаю, что это доступно в Java: почему целочисленное деление на ноль 1/0 дает ошибку, а с плавающей запятой 1 / 0.0 возвращает "Инф"?

  • странные операции, которые находятся на границе плюс или минус бесконечности, но дают + - бесконечность вместо NaN

    • 1.0f / 0.0f
    • log(0.0f)

    0.0 почти попадает в эту категорию, но, скорее всего, проблема в том, что он может переходить в плюс или минус бесконечность, поэтому он был оставлен как NaN.

  • если NaN является входом плавающей операции, выход также имеет тенденцию быть NaN

  • Есть несколько возможных значений для NaN 0x7fc00000, 0x7fc00001, 0x7fc00002, хотя x86_64 , кажется, генерировать только0x7fc00000 .

  • NaN и бесконечность имеют одинаковое двоичное представление.

    Разберем несколько из них:

    nan          = 0x7fc00000 = 0 11111111 10000000000000000000000
    positive_inf = 0x7f800000 = 0 11111111 00000000000000000000000
    negative_inf = 0xff800000 = 1 11111111 00000000000000000000000
                                | |        |
                                | |        mantissa
                                | exponent
                                |
                                sign

    Исходя из этого, мы подтверждаем, что указывает IEEE754:

    • и NaN, и бесконечности имеют показатель степени == 255 (все единицы)
    • бесконечности имеют мантиссу == 0. Следовательно, есть только две возможные бесконечности: + и -, дифференцированные битом знака.
    • NaN имеет мантиссу! = 0. Следовательно, есть несколько возможностей, за исключением мантиссы == 0, которая равна бесконечности.
  • NaN могут быть как положительными, так и отрицательными (верхний бит), хотя это не влияет на нормальные операции.

Протестировано в Ubuntu 18.10 amd64, OpenJDK 1.8.0_191.

Чиро Сантилли 郝海东 冠状 病 六四 事件 法轮功
источник
3

Не специалист по Java, но в JS и других языках я использую это «Не число», что означает, что какая-то операция заставила его стать недействительным числом.

Брайан Мейнс
источник
3

Это буквально означает «не число». Я подозреваю, что что-то не так с вашим процессом преобразования.

Ознакомьтесь с разделом Not A Number по этой ссылке.

Крис Томпсон
источник
3

Недействительное значение с плавающей запятой (например, результат деления на ноль)

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

Владимир Дюжев
источник
Я не согласен с этим ответом. Во-первых: «NaN» - это допустимое значение для числа с плавающей запятой IEEE! (В конце концов, он определен в спецификации ... так что он «действителен», не так ли?). Во-вторых: «деление на ноль» может быть представлено IEEE «Положительная бесконечность» или «отрицательная бесконечность»; лучший пример «NaN» - «ноль, деленный на ноль», как правильно указали некоторые другие ответы.
Dan H
«Действительное значение» и «определено в спецификации» - это не одно и то же. Согласовано с 0/0.
Владимир Дюжев