Java基础-double、float中的NaN、Infinity

数字运算除以0一定会报错吗

在Java中进行数字类型运算时,容易有一种误解:进行除法运算时,如果除数为0则会在运行时会抛出java.lang.ArithmeticException: / by zero的运行时异常。
但事实上并非如此,以double类型数据运算为例,看下面这段运算的结果:

1
2
3
4
5
6
7
8

@Test
public void DoubleTest() {
double d1 = 10D / 0;
double d2 = -10D / 0;
double d3 = 0.0D / 0;
System.out.println("d1=" + d1 + ", d2=" + d2 + ", d3=" + d3); //输出结果:d1=Infinity, d2=-Infinity, d3=NaN
}

输出结果:d1=Infinity, d2=-Infinity, d3=NaN。
这里的Infinity-InfinityNaN并不是字符串,而是double类型的常量,查看double类型对应的包装类的源码就可以看到。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public final class Double extends Number implements Comparable<Double> {

/**
* A constant holding the positive infinity of type
* {@code double}. It is equal to the value returned by
* {@code Double.longBitsToDouble(0x7ff0000000000000L)}.
*/
public static final double POSITIVE_INFINITY = 1.0 / 0.0;

/**
* A constant holding the negative infinity of type
* {@code double}. It is equal to the value returned by
* {@code Double.longBitsToDouble(0xfff0000000000000L)}.
*/
public static final double NEGATIVE_INFINITY = -1.0 / 0.0;

/**
* A constant holding a Not-a-Number (NaN) value of type
* {@code double}. It is equivalent to the value returned by
* {@code Double.longBitsToDouble(0x7ff8000000000000L)}.
*/
public static final double NaN = 0.0d / 0.0;




//......省略

}

上面几个 double 常量的意思:

  • POSITIVE_INFINITY(正无穷大),正数除以零得到正无穷。
  • NEGATIVE_INFINITY(负无穷大),负数除以零得到负无穷。
  • NaN(非数字),0除以0时得到非数字。 

所以就会导致一个问题,如果一个对象的属性值是NaN,在通过JDBC将对象插入到数据库中时,因为NaN对数据库而言并不是一个有效数字,就会报错:java.lang.IllegalArgumentException: NaN如果使用了MyBatis,会看到有Error setting non null for parameter #xxx with JdbcType DOUBLE这种异常。

NaN是什么

定义

NaN(Not a Number,非数)是计算机科学中数值数据类型的一个值,表示未定义或不可表示的值。常在浮点数运算中使用。首次引入NaN的是1985年的IEEE 754浮点数标准。

会返回NaN的运算

  • 操作数中至少有一个是 NaN 的运算:
  • 未定义的操作
    • 下列除法运算:0/0、∞/∞、∞/−∞、−∞/∞、−∞/−∞
    • 下列乘法运算:0×∞、0×-∞
    • 下列加法运算:∞ + (−∞)、(−∞) + ∞
    • 下列减法运算:∞ - ∞、(−∞) - (−∞)
  • 产生复数结果的实数运算。例如:
    • 对负数进行开方运算
    • 对负数进行对数运算
    • 对比-1小或比+1大的数进行反正弦或反余弦运算

Java中的NaN:

Java虚拟机在处理浮点数运算时,不会抛出任何运行时异常(这里所讲的是java语言中的异常,请勿与IEEE 754规范中的浮点异常相互混淆,IEEE 754的浮点异常是一种运算信号)。
如果一个操作产生溢出时,将会使用有符号的无穷大来表示;如果某个操作结果没有明确的数学定义的话,将会使用NaN值来表示,所有使用NaN值作为操作数的算术操作,结果都返回NaN。
Java中的Double和Float中都有isNaN函数,可以用来判断一个数是不是NaN,其实现是通过 v != v 的方式,因为NaN是唯一与自己不相等的值,NaN与任何值都不相等。(和NaN不同,Infinity和自己比时相等的。)

1
2
3
public static boolean isNaN(float v) {
return (v != v);
}

还有一点需要注意:如果将一个double或float的NaN转换为整数类型T(T限于int或long类型之一),将会转化为int或long类型的0;而double类型的NaN值将按规定转换为float类型的NaN值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

@Test
public void DoubleTest2() {
double d1 = 10D / 0;
double d2 = -10D / 0;
double d3 = 0.0D / 0;

System.out.println(new Double(d1).isNaN()); //输出结果:false
System.out.println(new Double(d3).isNaN()); //输出结果:true
System.out.println(d3 == d3); //输出结果:false

System.out.println((int) d1); //输出结果:2147483647
System.out.println((int) d3); //输出结果:0

}
------ 本文完 ------