自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Java.lang.NumberFormatException: Infinite or NaN,怎么破?

開發(fā) 前端
在Java中,?浮點(diǎn)數(shù)0并非一個(gè)準(zhǔn)確值,而是一個(gè)無(wú)限接近0的數(shù)。為此才鬧出這么多令人費(fèi)解的“幺蛾子”,這是由計(jì)算機(jī)底層原理決定的,記住就好,無(wú)解。

你好,我是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)鍵信息供以我們查找異常源頭:

  1. 異常類型:java.lang.NumberFormatException
  2. 異常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)行程序,輸出:

Infinity
-Infinity
NaN

總結(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)行程序,輸出:

true
true
false

結(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這玩意就是這么神奇的存在。

責(zé)任編輯:武曉燕 來(lái)源: YourBatman
相關(guān)推薦

2014-05-16 13:44:27

2015-07-27 11:35:15

2016-05-04 11:19:53

2015-02-01 15:52:27

2013-09-17 10:16:50

Infinite Mo傻瓜式

2021-02-09 08:31:38

線下環(huán)境 stable

2013-10-15 15:54:46

Windows XPWindows 7

2018-08-22 06:33:30

2024-06-25 15:46:41

OpenAIChatGPTGPT-4

2009-07-08 12:53:29

JDK源碼Java.lang.B

2022-08-10 14:52:02

DeepFakeAI

2018-03-15 10:36:30

2020-12-15 10:14:47

NumPynanPython

2015-04-01 10:07:06

云計(jì)算概念公有云私有云

2024-01-04 07:02:36

GoLangFiber開發(fā)

2022-07-18 13:35:24

數(shù)據(jù)安全網(wǎng)絡(luò)安全

2018-06-29 15:45:29

技術(shù)總監(jiān)管理者架構(gòu)設(shè)計(jì)

2022-10-30 14:34:30

數(shù)據(jù)業(yè)務(wù)

2017-03-22 12:13:36

AI神經(jīng)網(wǎng)絡(luò)模型算法
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)