你好,我是YourBatman:當我老了,也寫代碼;不為別的,只為愛好。
??前言
如果你工作超5年,100%遇到過這個異常:java.lang.NumberFormatException: Infinite or NaN

- Infinite中文釋義:極大的、無法衡量的、無窮盡的;
- NaN:Not a Number,不是一個數,它是計算機科學中數據類型的一種,代表不可表示的值,常用于浮點數計算中,于1985年納入浮點數標準IEEE 754。
在 Java 中只有浮點類型(Float&Double)實現了IEEE 754標準
它還有些變種異常:閱完本文就知道這些異常本質上其實是一回事了
- java.lang.NumberFormatException: For input string: NaN
- java.sql.SQLException: 'NaN' is not a valid numeric or approximate numeric value
?正文
java.lang.NumberFormatException: Infinite or NaN異常并不算常見(畢竟開發(fā)中浮點數遠遠沒有整數使用場景多),但也絕不罕見。so,知道為何會出現此異常,以及如何解決它是每個開發(fā)者必知必會的知識點。
??異常哪里拋出來的?
(假設你看不到異常棧)從拋出的異常中可以提取到兩個關鍵信息供以我們查找異常源頭:
- 異常類型:java.lang.NumberFormatException
- 異常detail msg:Infinite or NaN
首先當然是利用Java語言強類型的優(yōu)勢,看看哪些地方引用到了java.lang.NumberFormatExceptionNumberFormatException:

OMG,在641個地方出現過,看到這個數字該當場死心了:這條信息基本就是無效信息。
無奈再根據關鍵字Infinite or NaN搜索試試:

太幸運了,有且僅有一處代碼里存在??纯词悄睦铮?/p>

破案了:** java.lang.NumberFormatException: Infinite or NaN異常有且僅在構造BigDecimal實例的時候才有可能拋出。**
??拋出此異常的原因
既然拋出此異常的源碼都找到了,并且還只有一處,回答此問題就非常容易了:
public BigDecimal(double val, MathContext mc) {
if (Double.isInfinite(val) || Double.isNaN(val))
throw new NumberFormatException("Infinite or NaN");
... // 省略其它代碼
}
邏輯簡單,將Double的兩個方法isInfinite()和isNaN()一看便知:
public final class Double extends Number implements Comparable<Double> {
// 常量
public static final double POSITIVE_INFINITY = 1.0 / 0.0;
public static final double NEGATIVE_INFINITY = -1.0 / 0.0;
public static final double NaN = 0.0d / 0.0;
public static boolean isInfinite(double v) {
return (v == POSITIVE_INFINITY) || (v == NEGATIVE_INFINITY);
}
public static boolean isNaN(double v) {
return (v != v);
}
}
一個個來。
??isInfinite(double v)
public static final double POSITIVE_INFINITY = 1.0 / 0.0;
public static final double NEGATIVE_INFINITY = -1.0 / 0.0;
public static boolean isInfinite(double v) {
return (v == POSITIVE_INFINITY) || (v == NEGATIVE_INFINITY);
}
將v和兩個常量比較而已,邏輯不可謂不簡單。那么關鍵點來了:什么情況下一個double類型的值會和POSITIVE_INFINITY/NEGATIVE_INFINITY常量相等呢?
其實看Double類對這兩個常量的定義,就明白了(參考????常量定義代碼)。為了更清晰的對號入座,筆者這里再來幾個舉一反三的case:
@Test
public void fun2() {
// 等于Double.POSITIVE_INFINITY的場景
System.out.println(1.0 / 0 == Double.POSITIVE_INFINITY); // true
System.out.println(2.0 / 0 == Double.POSITIVE_INFINITY); // true
System.out.println(1 / 0.0 == Double.POSITIVE_INFINITY); // true
System.out.println(2 / 0.0 == Double.POSITIVE_INFINITY); // true
System.out.println(new Double(Double.POSITIVE_INFINITY) == Double.POSITIVE_INFINITY); // true
// 等于Double.NEGATIVE_INFINITY的場景
System.out.println(-1.0 / 0 == Double.NEGATIVE_INFINITY); // true
System.out.println(-2.0 / 0 == Double.NEGATIVE_INFINITY); // true
System.out.println(-1 / 0.0 == Double.NEGATIVE_INFINITY); // true
System.out.println(-2 / 0.0 == Double.NEGATIVE_INFINITY); // true
System.out.println(new Double(Double.NEGATIVE_INFINITY) == Double.NEGATIVE_INFINITY); // true
// 需特別注意的特殊case:
System.out.println(1.0 / -0 == Double.POSITIVE_INFINITY); // -0和0沒有區(qū)別,所以結果是POSITIVE(true)
System.out.println(1.0 / -0.0 == Double.NEGATIVE_INFINITY); // -0.0和0.0是有區(qū)別的,所以結果是POSITIVE(false)
}
總結一下:浮點數除法運算,分母為0且分子不為0,結果就是POSITIVE_INFINITY/NEGATIVE_INFINITY。
Tips:它哥兩分別稱作正無窮大和負無窮大
??isNaN(double v)
public static final double NaN = 0.0d / 0.0;
public static boolean isNaN(double v) {
return (v != v);
}
what?自己還能不等于自己?bug吧~
來看看:
@Test
public void fun3() {
// double d = 0.0d / 0; // 結果一樣
System.out.println(d == Double.NaN);
System.out.println(Double.isNaN(d));
}
運行后的輸出結果為:
false
false -> d==d這個是false喲
true
驚不驚喜,意不意外:還真存在自己不等于自己的情況呢。
總結一下:浮點數除法計算,分母為0且分子為0,結果就是NaN。并且:每次計算的NaN都永不相等。
Tips:NaN代表不是數字,因此“不是數字”和“不是數字”不相等,從邏輯上好像也說得通嘛
??針對此異常的補充說明
圍繞POSITIVE_INFINITY、NEGATIVE_INFINITY、NaN三個常量進行一些補充說明吧。
??直接打印輸出什么?
@Test
public void fun4() {
System.out.println(Double.POSITIVE_INFINITY);
System.out.println(Double.NEGATIVE_INFINITY);
System.out.println(Double.NaN);
}
運行程序,輸出:
總結一下:Double對象打印輸出(toString或者序列化),不一定永遠是數字,也有可能是字符串。
?? 是否可以參與運算和比較?
雖然是常量,但畢竟也是數字類型嘛,那就看看運算和比較嘍:
運算:
@Test
public void fun7() {
System.out.println("正無窮大參與運算:" + (Double.POSITIVE_INFINITY + 1)); // Infinity
System.out.println("正無窮大參與運算:" + (Double.POSITIVE_INFINITY - 1)); // Infinity
System.out.println("負無窮大參與運算:" + (Double.NEGATIVE_INFINITY * 1)); // -Infinity
System.out.println("負無窮大參與運算:" + (Double.NEGATIVE_INFINITY / 1)); // -Infinity
System.out.println("負無窮大參與運算:" + (Double.NEGATIVE_INFINITY / 0)); // -Infinity
System.out.println("NaN參與運算:" + (Double.NaN + 1)); // NaN
System.out.println("NaN參與運算:" + (Double.NaN - 1)); // NaN
System.out.println("NaN參與運算:" + (Double.NaN * 1)); // NaN
System.out.println("NaN參與運算:" + (Double.NaN / 1)); // NaN
System.out.println("NaN參與運算:" + (Double.NaN / 0)); // NaN
// 特殊場景
System.out.println(Double.POSITIVE_INFINITY - Double.POSITIVE_INFINITY); // NaN
System.out.println(Double.NEGATIVE_INFINITY - Double.NEGATIVE_INFINITY); // NaN
System.out.println(Double.POSITIVE_INFINITY + Double.NEGATIVE_INFINITY); // NaN
System.out.println("負無窮大參與運算:" + (Double.POSITIVE_INFINITY / -0.0)); // -Infinity
System.out.println("負無窮大參與運算:" + (Double.NEGATIVE_INFINITY / -0.0)); // Infinity
}
總結一下:正/負無窮大和任何數值(包括除以0)做運算結果都是本身,和Infinite or NaN運算結果為NaN;NaN進行任何運算的結果都是NaN。
特例:正/負無窮大若除以-0的話,結果互調
比較:
@Test
public void fun6() {
System.out.println("正無窮大 > 任何數嗎? -> " + (Double.POSITIVE_INFINITY > Double.MAX_VALUE)); // true
System.out.println("正無窮大 > 任何數嗎? -> " + (Double.POSITIVE_INFINITY > Long.MAX_VALUE)); // true
System.out.println("負無窮大 < 任何數嗎? -> " + (Double.POSITIVE_INFINITY > Double.MIN_VALUE)); // true
System.out.println("負無窮大 < 任何數嗎? -> " + (Double.POSITIVE_INFINITY > Long.MIN_VALUE)); // true
System.out.println("NaN參與比較:" + (Double.NaN == Double.NaN)); // false
System.out.println("NaN參與比較:" + (Double.NaN > Double.NaN)); // false
System.out.println("NaN參與比較:" + (Double.NaN < Double.NaN)); // false
System.out.println("NaN參與比較:" + (Double.NaN < 1)); // false
System.out.println("NaN參與比較:" + (Double.NaN < -1)); // false
System.out.println("NaN參與比較:" + (Double.NaN != -1)); // true
System.out.println("NaN參與比較:" + (Double.NaN != Double.NaN)); // true
}
總結一下:正無窮大比任何數值都大;負無窮大比任何數值都??;NaN參與!=比較永遠是true(包括和自己比),除此之外都為false。
?? Float里的這三個常量和Double一樣嗎?
弱弱問一句:2023年了在實際業(yè)務開發(fā)中,不會真有人使用Float吧?吧?吧?
靈魂拷問:如果你使用了Float,收益是什么?是否真的值得?
Float類里也存在這三個常量和判斷的方法:
public final class Float extends Number implements Comparable<Float> {
// 常量
public static final float POSITIVE_INFINITY = 1.0f / 0.0f;
public static final float NEGATIVE_INFINITY = -1.0f / 0.0f;
public static final float NaN = 0.0f / 0.0f;
public static boolean isInfinite(float v) {
return (v == POSITIVE_INFINITY) || (v == NEGATIVE_INFINITY);
}
public static boolean isNaN(float v) {
return (v != v);
}
}
和Double可謂一毛一樣嘛??聪逻@個:
@Test
public void fun5() {
System.out.println(Double.POSITIVE_INFINITY == Float.POSITIVE_INFINITY);
System.out.println(Double.NEGATIVE_INFINITY == Float.NEGATIVE_INFINITY);
System.out.println(Double.NaN == Float.NaN);
}
運行程序,輸出:
結論無需多言,自行體會做到心中有數哈。
?? 其它語言的表現
以弱類型語言JavaScript為例:

表現和Java一樣。畢竟NaN早已被納入IEEE 754規(guī)范了,不出意外每種編程語言的表現都是一致的。
Tips:JavaScript中的isFinite()方法是正向思維的,和Java里isInfinite()是“反”著來的哦
??遇到此異常怎么破?
解決問題的難度永遠在根因定位上,至于遇到此異常怎么破嘛,略?。?!
考慮到代碼的健壯性,實際場景中是可以對這些異常做預處理的:使用Double.isNaN()、Double.isInfinite()等方法來做分支邏輯
??總結
在Java中,浮點數0并非一個準確值,而是一個無限接近0的數。為此才鬧出這么多令人費解的“幺蛾子”,這是由計算機底層原理決定的,記住就好,無解。
計算機的運算基于數學,但貌似也有些“不同于”數學理論。這不,NaN這玩意就是這么神奇的存在。