在Java中,NaN是什么意思?


107

我有一个程序试图缩小double到所需的数量。我得到的输出是NaN

NaN在Java 中是什么意思?


在Java中使用NaN时,对NaN和常见陷阱有一个很好的描述:ppkwok.blogspot.co.uk/2012/11/…–
Phil

如果您问“ NaN有什么好处?” 在Java(或其他任何语言)中,我可以为您提供一个非常方便的用例:当我有一个浮点数的二维数组时,但是对于该二维数组的某些部分,我的计算没有任何意义,我将用“ NaN”填充该值。这可以用来向我的计算的下游用户发出信号(例如,当它变成光栅图像时)“此时不要注意该值”。很有用!
丹·H

顺便说一句,确切地说,“缩小”两倍意味着什么?好奇...
Dan H

Answers:


153

取自此页面

“ NaN”代表“不是数字”。如果浮点运算具有一些输入参数,导致该运算产生一些未定义的结果,则会生成“ Nan”。例如,0.0除以0.0在算术上是不确定的。负数的平方根也是不确定的。


16
此外,NaN是由IEEE浮点算术标准(IEEE 754)明确定义的,盲目遵循Java。阅读标准会使您大开眼界,零的多个值就是其中之一。
Esko

37
同样,它NaN具有有趣的特性,它是唯一的“数字”,在比较时与自身不同。因此,一个普通的(并且在许多语言中是唯一的)测试数字x是否NaN如下:boolean isNaN(x){return x != x;}
quazgar 2013年

3
答案链接已死?

3
...“未定义负数的平方根(在算术中)” ...不是!其实际上i与像蟒蛇处理一些语言非常好,它...它可能不是在的情况下java
拉斐尔ŧ

5
@RafaelT我会说在非复杂算术中它是未定义的。在Java中,无法将复数分配给float或double。Python是动态类型的,因此在这种情况下,可能只返回一个复数。
sstn 2014年

19

NaN表示“不是数字”,基本上表示IEE 754浮点标准中的特殊浮点值。N通常表示该值不能用有效的浮点数表示。

当要转换的值是其他值时,例如当转换不代表数字的字符串时,转换将产生该值。


如何转换?用parseFloat()parseDouble?或者是其他东西?
阿隆索·德尔·阿特

14

NaN表示“不是数字”,并且是对浮点数进行未定义操作的结果,例如将零除以零。(请注意,虽然在数学中通常也未定义非零数字除以零,但它不会导致NaN而是正或负无穷大)。


5

NaN表示“不是数字”。这是一个特殊的浮点值,表示操作的结果未定义或无法表示为实数。

有关此值的更多说明,请参见此处




4

表示不是数字。它是许多编程语言中不可能的数值的常见表示形式。


4

最小的可运行示例

您必须了解的第一件事是,NaN的概念直接在CPU硬件上实现。

所有主要的现代CPU似乎都遵循IEEE 754,该标准规定了浮点格式,而NaN(只是特殊的浮点值)是该标准的一部分。

因此,该概念在所有语言中都非常相似,包括Java,Java只是直接向CPU发送浮点代码。

在继续之前,您可能需要先阅读我写的以下答案:

现在进行一些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返回“ Inf”?

  • 加上正负无穷大的怪异运算,但确实给出了+-无限而不是NaN

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

    0.0 几乎属于这一类,但可能的问题是它可以达到正负无穷大,因此将其保留为NaN。

  • 如果NaN是浮动运算的输入,则输出也往往是NaN

  • 有几种可能的值楠0x7fc000000x7fc000010x7fc00002,虽然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(全部为1)
    • 无限式的尾数==0。因此,只有两种可能的无限式:+和-,由符号位来区分
    • NaN的尾数!=0。因此,除了尾数== 0(无穷大)外,还有几种可能性
  • NaN可以是正数或负数(最高位),尽管它对正常操作没有影响

在Ubuntu 18.10 amd64,OpenJDK 1.8.0_191中进行了测试。


3

不是Java的人,但是在JS和其他语言中,我使用的是“不是数字”,这意味着某些操作导致它成为无效数字。



3

不是有效的浮点值(例如,被零除的结果)

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


我对此答案表示怀疑。第一:“ NaN”是IEEE浮点数的有效值!(毕竟,它是在规范中定义的……所以它的“有效”,对吧?)。第二:“除以零”可以用IEEE“正无穷大”或“负无穷大”表示;正如其他答案正确指出的那样,“ NaN”的更好示例是“零除以零”。
Dan H

“有效值”和“规范中定义”不是同一回事。同意为0/0。
弗拉基米尔·朱热夫(Fladimir Dyuzhev),
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.