在Java的浮点数运算体系中,float和double类型除了可以表示常规的数值之外,还存在NaN和Infinity两类特殊值。NaN即Not a Number,代表非数字结果,Infinity则代表无穷大,这两类值通常出现在不符合数学规则的运算场景中,比如零作为除数的除法、对负数取平方根等操作,如果程序没有对这些特殊值做判断处理,可能会让后续的计算逻辑出现不符合预期的结果。

NaN和Infinity的产生场景
要处理这两类特殊值,首先需要明确它们会在哪些场景下出现,常见的产生场景如下:
- 对负数执行开平方根运算,比如Math.sqrt(-1),结果会返回NaN
- 零作为除数的除法运算,比如1.0 / 0.0,结果会返回Infinity,如果是-1.0 / 0.0则返回-Infinity
- 浮点数运算结果超出double或float的表示范围,也会产生Infinity
- NaN参与的任何运算,结果依然是NaN
判断NaN和Infinity的方法
判断NaN
NaN有一个特殊的性质:它和任何值都不相等,包括它自己。因此不能使用==运算符判断一个值是否为NaN,Java中提供了标准判断方式:
- 使用Double或Float类的isNaN()方法,这是最推荐的方式
- 利用NaN不等于自身的特性,通过value != value的方式判断
public class NaNCheckDemo {
public static void main(String[] args) {
double result = Math.sqrt(-1);
// 使用Double.isNaN()判断
boolean isNan1 = Double.isNaN(result);
// 利用NaN不等于自身的特性判断
boolean isNaN2 = result != result;
System.out.println("isNaan1判断结果:" + isNaN1);
System.out.println("isNaan2判断结果:" + isNaN2);
}
}
判断Infinity
判断Infinity可以使用Double或Float类的isInfinite()方法,也可以通过和Double.POSITIVE_INFINITY、Double.NEGATIVE_INFINITY比较的方式判断:
public class InfinityCheckDemo {
public static void main(String[] args) {
double positiveInfinity = 1.0 / 0.0;
double negativeInfinity = -1.0 / 0.0;
// 使用Double.isInfinite()判断是否为无穷大(包括正负无穷大)
boolean isInfinite1 = Double.isInfinite(positiveInfinity);
boolean isInfinite2 = Double.isInfinite(negativeInfinity);
// 判断正无穷大
boolean isPositive = positiveInfinity == Double.POSITIVE_INFINITY;
// 判断负无穷大
boolean isNegative = negativeInfinity == Double.NEGATIVE_INFINITY;
System.out.println("正无穷大isInfinite判断结果:" + isInfinite1);
System.out.println("负无穷大isInfinite判断结果:" + isInfinite2);
System.out.println("是否为正无穷大:" + isPositive);
System.out.println("是否为负无穷大:" + isNegative);
}
}
NaN和Infinity的处理方式
当检测到浮点数结果为NaN或Infinity时,需要根据业务场景做对应的处理,常见的处理方式有以下几种:
- 设置默认值:如果特殊值不影响核心逻辑,可以将其替换为预设的默认数值,比如替换为0或者业务允许的边界值
- 抛出异常:如果特殊值代表输入参数非法,可以直接抛出IllegalArgumentException等异常,提示调用方参数错误
- 记录日志并返回空值:如果特殊值出现在非核心流程中,可以记录异常日志,然后返回null或者空的Optional对象
public class HandleSpecialValueDemo {
public static double safeDivide(double a, double b) {
double result = a / b;
// 判断是否为NaN
if (Double.isNaN(result)) {
System.out.println("运算结果为非数字,返回默认值0");
return 0.0;
}
// 判断是否为无穷大
if (Double.isInfinite(result)) {
if (result == Double.POSITIVE_INFINITY) {
System.out.println("运算结果为正无穷大,返回最大值");
return Double.MAX_VALUE;
} else {
System.out.println("运算结果为负无穷大,返回最小值");
return -Double.MAX_VALUE;
}
}
return result;
}
public static void main(String[] args) {
System.out.println(safeDivide(1.0, 0.0));
System.out.println(safeDivide(-1.0, 0.0));
System.out.println(safeDivide(0.0, 0.0));
System.out.println(safeDivide(10.0, 2.0));
}
}
注意事项
在开发中还需要注意几个和NaN、Infinity相关的细节:
- 基本类型的包装类Double和Float的equals()方法可以正确判断NaN和Infinity,但是不建议用==判断,因为浮点数的精度问题可能导致误判
- 如果业务中对数值合法性要求很高,建议在浮点数运算前先对入参做校验,避免产生特殊值
- NaN和Infinity在序列化、网络传输时可能会被特殊处理,需要做对应的兼容逻辑