题
我有一个程序试图缩小 double
下降到所需的数字。我得到的输出是 NaN
.
什么是 NaN
在Java中是什么意思?
解决方案
这此页摘自:
“南”代表“非数字”。 “南” 产生,如果一个浮点 操作具有一些输入参数 使所述操作以产生 一些不确定的结果。例如, 0.0通过0.0除以算术未定义。服用的平方根 负数也是未定义的。
其他提示
NaN
方法 “不是一个数字” 基本上是特殊浮点值的表示 IEE 754 浮点 标准。 南 通常意味着该值无法用有效的浮点数表示。
当要转换的值是其他值时(例如转换不代表数字的字符串时),转换将产生此值。
NaN
装置“不是数”和是未定义的操作对浮点数像例如零被零除的结果。 (注意的是,虽然将一个非零数通过零也通常在数学未定义,它不会导致的NaN但在正或负无穷大)。
NaN
装置 “而不是数字。”它是该装置的操作的结果没有定义或不是表示为一个实数特殊浮点值。
的NaN表示不是数字。它是用于表示在数学上未定义的任何价值。像分割0.0的0.0。 你可以看看这里的信息的 https://web.archive.org/web/20120819091816/http://www.concentric.net/~ttwang/tech/javafloat.htm
在这里发表您的程序,如果你需要更多的帮助。
的NaN =不是数字。
平均值不是数字。 这是在许多编程语言不可能数值的通用表示。
不是Java的家伙,但在JS和其他语言我用它的“非数”,意思是某些操作导致它变得不是有效的数字。
有字面意思是“不是数字。”我怀疑事情是不对您的转换过程。
检查出在此参考
不是一个有效的浮点值(例如被零除的结果)
最小可运行示例
您必须了解的第一件事是 NaN 的概念是直接在 CPU 硬件上实现的。
所有主要的现代 CPU 似乎都在效仿 IEEE 754 它指定浮点格式,而 NaN(只是特殊的浮点值)是该标准的一部分。
因此,这个概念在任何语言中都非常相似,包括 Java,它只是直接向 CPU 发出浮点代码。
在继续之前,您可能需要先阅读我写的以下答案:
- 快速回顾一下 IEEE 754 浮点格式: 什么是次正规浮点数?
- 使用 C / C++ 涵盖的一些较低级别的 NaN 基础知识: 安静 NaN 和信号 NaN 有什么区别?
现在进行一些 Java 操作。大多数不在核心语言中的感兴趣的功能都位于内部 java.lang.Float
.
南.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));
}
}
运行:
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
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 中测试。