1. 概述

简单来说,NaN(Not a Number)是一个特殊的数值类型,表示“非数字”。

本文将深入讲解 Java 中的 NaN 值,包括哪些操作会产生它、如何正确判断和处理它。对于做数值计算或数据清洗的同学来说,这属于必须掌握的“踩坑”知识点。

2. 什么是 NaN?

NaN 通常表示无效运算的结果。比如 0.0 / 0.0 这种数学上无定义的操作,Java 不会抛异常,而是返回 NaN。

也用于表示无法用实数表达的值。例如 √(-1) 在实数范围内无解,Java 就用 NaN 表示。

NaN 的定义来自 IEEE 754 浮点数标准,Java 的 floatdouble 类型完全遵循这一标准。

Java 提供了两个常量:

  • Double.NaN
  • Float.NaN

官方文档描述如下:

“一个表示 double 类型 NaN 值的常量。其值等价于 Double.longBitsToDouble(0x7ff8000000000000L)。”

“一个表示 float 类型 NaN 值的常量。其值等价于 Float.intBitsToFloat(0x7fc00000)。”

⚠️ 注意:Java 其他数值类型(如 intlong)没有对应的 NaN 常量。

3. NaN 的比较问题

这是最容易踩坑的地方:NaN 无法通过常规比较操作识别

所有涉及 NaN 的比较(==><)都返回 false,唯独 != 返回 true

来看一段代码:

double NAN = Double.NaN;

System.out.println("NaN == 1 = " + (NAN == 1));
System.out.println("NaN > 1 = " + (NAN > 1));
System.out.println("NaN < 1 = " + (NAN < 1));
System.out.println("NaN != 1 = " + (NAN != 1));
System.out.println("NaN == NaN = " + (NAN == NAN));
System.out.println("NaN > NaN = " + (NAN > NAN));
System.out.println("NaN < NaN = " + (NAN < NAN));
System.out.println("NaN != NaN = " + (NAN != NAN));

输出结果:

NaN == 1 = false
NaN > 1 = false
NaN < 1 = false
NaN != 1 = true
NaN == NaN = false
NaN > NaN = false
NaN < NaN = false
NaN != NaN = true

看到没?连 NaN == NaN 都是 false!所以:

❌ 错误做法:

if (value == Double.NaN) { ... } // 永远不会成立

✅ 正确做法有两种:

方法一:利用 x != x 只对 NaN 成立

if (value != value) {
    // 只有 NaN 满足这个条件
}

方法二(推荐):使用标准 API

double x = 1;
System.out.println(x + " is NaN = " + (x != x));
System.out.println(x + " is NaN = " + Double.isNaN(x));
        
x = Double.NaN;
System.out.println(x + " is NaN = " + (x != x));
System.out.println(x + " is NaN = " + Double.isNaN(x));

输出:

1.0 is NaN = false
1.0 is NaN = false
NaN is NaN = true
NaN is NaN = true

📌 建议:优先使用 Double.isNaN()Float.isNaN(),代码更清晰,可读性更强。

4. 哪些操作会产生 NaN?

处理浮点数时,以下几种情况会返回 NaN 而不是抛出异常,必须小心!

4.1 数学上无定义的运算

double ZERO = 0;
System.out.println("ZERO / ZERO = " + (ZERO / ZERO));
System.out.println("INFINITY - INFINITY = " + 
  (Double.POSITIVE_INFINITY - Double.POSITIVE_INFINITY));
System.out.println("INFINITY * ZERO = " + (Double.POSITIVE_INFINITY * ZERO));

输出:

ZERO / ZERO = NaN
INFINITY - INFINITY = NaN
INFINITY * ZERO = NaN

4.2 实数范围内无解的函数

System.out.println("SQUARE ROOT OF -1 = " + Math.sqrt(-1));
System.out.println("LOG OF -1 = " +  Math.log(-1));

输出:

SQUARE ROOT OF -1 = NaN
LOG OF -1 = NaN

4.3 任何与 NaN 的运算

只要参与运算的任意操作数是 NaN,结果就是 NaN:

System.out.println("2 + NaN = " +  (2 + Double.NaN));
System.out.println("2 - NaN = " +  (2 - Double.NaN));
System.out.println("2 * NaN = " +  (2 * Double.NaN));
System.out.println("2 / NaN = " +  (2 / Double.NaN));

输出:

2 + NaN = NaN
2 - NaN = NaN
2 * NaN = NaN
2 / NaN = NaN

4.4 用 NaN 表示缺失值

Java 中不能给 doublefloatnull,所以常使用 NaN 来表示“未知”或“缺失”:

double maxValue = Double.NaN; // 表示尚未计算或无效

这在数据处理、统计计算中很常见,比如某传感器未采集到数据时,用 NaN 填充。

5. 总结

  • NaN 是 IEEE 754 标准定义的特殊值,Java 的 float/double 支持。
  • ❌ 不要用 == 判断 NaN,永远返回 false。
  • ✅ 使用 Double.isNaN()Float.isNaN() 进行判断。
  • 数学无定义、实数无解、含 NaN 的运算都会返回 NaN。
  • 可用 NaN 表示缺失值,替代 null 的语义。

处理浮点数时,特别是做数据聚合、比较、转换前,务必先检查 NaN,否则可能引发后续逻辑错误。

完整示例代码见 GitHub:https://github.com/eugenp/tutorials/tree/master/core-java-modules/core-java-numbers-2


原始标题:NaN in Java