浮點精度問題是怎么產(chǎn)生的
對于小數(shù)的運算,相信大家都有遇到過精度丟失問題,利于0.1+0.2得到的是0.30000000000000004而不是0.3,那么如何解釋為什么計算機中 0.2 + 0.1 不等于 0.3 呢?在剖析這個問題之前我們要先理解IEEE754標準。
什么是IEEE754?下面是一段官方的解釋了解即可,重點是我們要關(guān)注IEEE754存儲格式。
概念:IEEE二進制浮點數(shù)算術(shù)標準(IEEE754)是20世紀80年代以來最廣泛使用的浮點數(shù)運算標準,為許多CPU與浮點運算器所采用。這個標準定義了表示浮點數(shù)的格式(包括負零-0)與反常值(denormal number),一些特殊數(shù)值(無窮∞與非數(shù)值NaN),以及這些數(shù)值的“浮點數(shù)運算符”。 常見的四種浮點數(shù)值表示方式:單精確度(32位)、雙精確度(64位)、延伸單精確度(43比特以上,很少使用)與延伸雙精確度(79比特以上,通常以80位實現(xiàn))。C語言的float通常是指IEEE單精確度,而double是指雙精確度。
存儲格式:IEEE 754標準準確地定義了單精度和雙精度浮點格式
單精度浮點格式(32 位)。
雙精度浮點格式(64 位)。
EEE754 標準中規(guī)定 float 單精度浮點數(shù)在機器中表示用 1 位表示數(shù)字的符號,用 8 位表示指數(shù),用 23 位表示尾數(shù),即小數(shù)部分。對于 double 雙精度浮點數(shù),用 1 位表示符號,用 11 位表示指數(shù),52 位表示尾數(shù),其中指數(shù)域稱為階碼。IEEE754 浮點數(shù)的格式如下圖所示。

下面以0.1和0.2為例將其轉(zhuǎn)換為IEEE754的格式存儲到內(nèi)存中
0.1這個浮點數(shù)轉(zhuǎn)換成一個二進制數(shù)0.00011001100110011...
0.2這個浮點數(shù)轉(zhuǎn)換成一個二進制數(shù)0.0011001100110011...
這兩個浮點數(shù)無法精確轉(zhuǎn)換成一個二進制數(shù),由于在內(nèi)存中表示精度有限必須舍棄后面的尾數(shù)部分。
單精度為例0.1和0.2轉(zhuǎn)換為IEEE754格式以如下
0.1 : 0 01111011 10011001100110011001100
0.2:? 0 01111100 10011001100110011001100


所以我們看到0.1+0.2得到的是0.300000004而不是0.3的結(jié)論,而造成這個精度問題的根本原因在于不是所有的數(shù)字(如例子中的0.1和0.2)都可以用二進制表示而進行截斷,造成精度丟失。
但是盡管如此我們還是有辦法在運算是避免精度問題,那就是BigDecimal,那么BigDecimal是怎么解決這個問題的呢?
解決方案就是不使用用二進制,而是使用十進制(BigInteger)+小數(shù)點位置(scale)來表示小數(shù),所有的十進制數(shù)字都可以用這種形式來表示,比如 0.1? =1*10^-1? ? ? scale=1
BigDecimal 運算分成兩部分? ,BigInteger部分和更新scale即可,具體有興趣的同學可以翻翻BigDecimal 的源碼,在這里不再展開了。