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

- Infinite中文釋義:極大的、無(wú)法衡量的、無(wú)窮盡的;
- NaN:Not a Number,不是一個(gè)數(shù),它是計(jì)算機(jī)科學(xué)中數(shù)據(jù)類型的一種,代表不可表示的值,常用于浮點(diǎn)數(shù)計(jì)算中,于1985年納入浮點(diǎn)數(shù)標(biāo)準(zhǔn)IEEE 754。
在 Java 中只有浮點(diǎn)類型(Float&Double)實(shí)現(xiàn)了IEEE 754標(biāo)準(zhǔn)
它還有些變種異常:閱完本文就知道這些異常本質(zhì)上其實(shí)是一回事了
- 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ā)中浮點(diǎn)數(shù)遠(yuǎn)遠(yuǎn)沒(méi)有整數(shù)使用場(chǎng)景多),但也絕不罕見。so,知道為何會(huì)出現(xiàn)此異常,以及如何解決它是每個(gè)開發(fā)者必知必會(huì)的知識(shí)點(diǎn)。
??異常哪里拋出來(lái)的?
(假設(shè)你看不到異常棧)從拋出的異常中可以提取到兩個(gè)關(guān)鍵信息供以我們查找異常源頭:
- 異常類型:java.lang.NumberFormatException
- 異常detail msg:Infinite or NaN
首先當(dāng)然是利用Java語(yǔ)言強(qiáng)類型的優(yōu)勢(shì),看看哪些地方引用到了java.lang.NumberFormatExceptionNumberFormatException:

OMG,在641個(gè)地方出現(xiàn)過(guò),看到這個(gè)數(shù)字該當(dāng)場(chǎng)死心了:這條信息基本就是無(wú)效信息。
無(wú)奈再根據(jù)關(guān)鍵字Infinite or NaN搜索試試:

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

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

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