浮點(diǎn)精度問(wèn)題是怎么產(chǎn)生的
對(duì)于小數(shù)的運(yùn)算,相信大家都有遇到過(guò)精度丟失問(wèn)題乾颁,利于0.1+0.2得到的是0.30000000000000004而不是0.3,那么如何解釋為什么計(jì)算機(jī)中 0.2 + 0.1 不等于 0.3 呢艺栈?在剖析這個(gè)問(wèn)題之前我們要先理解IEEE754標(biāo)準(zhǔn)英岭。
什么是IEEE754?下面是一段官方的解釋了解即可湿右,重點(diǎn)是我們要關(guān)注IEEE754存儲(chǔ)格式诅妹。
概念:IEEE二進(jìn)制浮點(diǎn)數(shù)算術(shù)標(biāo)準(zhǔn)(IEEE754)是20世紀(jì)80年代以來(lái)最廣泛使用的浮點(diǎn)數(shù)運(yùn)算標(biāo)準(zhǔn),為許多CPU與浮點(diǎn)運(yùn)算器所采用。這個(gè)標(biāo)準(zhǔn)定義了表示浮點(diǎn)數(shù)的格式(包括負(fù)零-0)與反常值(denormal number)吭狡,一些特殊數(shù)值(無(wú)窮∞與非數(shù)值NaN)尖殃,以及這些數(shù)值的“浮點(diǎn)數(shù)運(yùn)算符”。 常見的四種浮點(diǎn)數(shù)值表示方式:?jiǎn)尉_度(32位)划煮、雙精確度(64位)送丰、延伸單精確度(43比特以上,很少使用)與延伸雙精確度(79比特以上弛秋,通常以80位實(shí)現(xiàn))器躏。C語(yǔ)言的float通常是指IEEE單精確度,而double是指雙精確度蟹略。
存儲(chǔ)格式:IEEE 754標(biāo)準(zhǔn)準(zhǔn)確地定義了單精度和雙精度浮點(diǎn)格式
單精度浮點(diǎn)格式(32 位)登失。
雙精度浮點(diǎn)格式(64 位)。
EEE754 標(biāo)準(zhǔn)中規(guī)定 float 單精度浮點(diǎn)數(shù)在機(jī)器中表示用 1 位表示數(shù)字的符號(hào)科乎,用 8 位表示指數(shù)壁畸,用 23 位表示尾數(shù),即小數(shù)部分茅茂。對(duì)于 double 雙精度浮點(diǎn)數(shù)捏萍,用 1 位表示符號(hào),用 11 位表示指數(shù)空闲,52 位表示尾數(shù)令杈,其中指數(shù)域稱為階碼。IEEE754 浮點(diǎn)數(shù)的格式如下圖所示碴倾。
下面以0.1和0.2為例將其轉(zhuǎn)換為IEEE754的格式存儲(chǔ)到內(nèi)存中
0.1這個(gè)浮點(diǎn)數(shù)轉(zhuǎn)換成一個(gè)二進(jìn)制數(shù)0.00011001100110011...
0.2這個(gè)浮點(diǎn)數(shù)轉(zhuǎn)換成一個(gè)二進(jìn)制數(shù)0.0011001100110011...
這兩個(gè)浮點(diǎn)數(shù)無(wú)法精確轉(zhuǎn)換成一個(gè)二進(jì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é)論跌榔,而造成這個(gè)精度問(wèn)題的根本原因在于不是所有的數(shù)字(如例子中的0.1和0.2)都可以用二進(jìn)制表示而進(jìn)行截?cái)嘁煅悖斐删葋G失。
但是盡管如此我們還是有辦法在運(yùn)算是避免精度問(wèn)題僧须,那就是BigDecimal纲刀,那么BigDecimal是怎么解決這個(gè)問(wèn)題的呢?
解決方案就是不使用用二進(jìn)制担平,而是使用十進(jìn)制(BigInteger)+小數(shù)點(diǎn)位置(scale)來(lái)表示小數(shù),所有的十進(jìn)制數(shù)字都可以用這種形式來(lái)表示示绊,比如 0.1? =1*10^-1? ? ? scale=1
BigDecimal 運(yùn)算分成兩部分? ,BigInteger部分和更新scale即可暂论,具體有興趣的同學(xué)可以翻翻BigDecimal 的源碼面褐,在這里不再展開了。