?閱讀本文大約需要8分鐘...
問(wèn)題
在計(jì)算機(jī)的世界里,可能有很多常人無(wú)法理解的事情。比如 0.1 + 0.2 = ?。來(lái),告訴我你的答案。
有的朋友看到這就迫不及待的說(shuō),這么簡(jiǎn)單的問(wèn)題,很明顯等于 0.3 啊,小學(xué)生都會(huì)算的好伐。你這是在侮辱我的智商?
好吧,我來(lái)告訴你一個(gè)打臉的事實(shí),0.1 + 0.2 還真不等于 0.3 。先別急著反駁我。
打開(kāi)你的任意一個(gè)瀏覽器(我用chrome做演示),F(xiàn)12打開(kāi)console控制臺(tái),輸入 console.log(0.1 + 0.2)
。如果你操作正確的話,你會(huì)看到以下的結(jié)果。
是不是感覺(jué)匪夷所思,what?為什么結(jié)果是0.30000000000000004,這是神魔鬼? 難道,我這么多年學(xué)習(xí)的數(shù)學(xué)知識(shí),老師教的都是錯(cuò)的?
別著急。其實(shí),你的老師教的沒(méi)錯(cuò),在我們的世界中,0.1 + 0.2 確實(shí)是等于 0.3 的。但是,在計(jì)算機(jī)中,可就不是這么一回事了。待我娓娓道來(lái)。
因?yàn)?,我們?cè)谟?jì)算數(shù)學(xué)問(wèn)題的時(shí)候,用的是十進(jìn)制,計(jì)算出來(lái)結(jié)果是0.3沒(méi)問(wèn)題。但是,在計(jì)算機(jī)中用的是二進(jìn)制,都是由0和1來(lái)組成。這就不得不提一下,十進(jìn)制轉(zhuǎn)換二進(jìn)制了。
二進(jìn)制轉(zhuǎn)換
十進(jìn)制小數(shù)轉(zhuǎn)換二進(jìn)制的步驟:(以10.25為例)
1.先轉(zhuǎn)換整數(shù)部分,除2直到商為0,倒數(shù)取余。
10/2 ... 商5...余數(shù)0 5/2 ...商2...余數(shù)1 2/2 ...商1...余數(shù)0 1/2 ...商0...余數(shù)1
倒數(shù)取余,就是1010
2.再轉(zhuǎn)換小數(shù)部分,乘2取整,直到小數(shù)部分為0.
0.25*2 ... 0.50 ...整數(shù)0 0.50*2 ... 1.0 ...整數(shù)1
小數(shù)部分為0,結(jié)束,即為01
因此10.25(10)轉(zhuǎn)換成二進(jìn)制,結(jié)果就是 1010.01(2)
聰明的你,類比以上方法,應(yīng)該可以動(dòng)手去算一下十進(jìn)制0.1轉(zhuǎn)成二進(jìn)制是多少了。
0.1*2 ... 0.2 ...整數(shù)0 0.2*2 ... 0.4 ...整數(shù)0 0.4*2 ... 0.8 ...整數(shù)0 0.8*2 ... 1.6
...整數(shù)1 0.6*2 ... 1.2 ...整數(shù)1 0.2*2 ... 0.4 ...整數(shù)0
等等,怎么感覺(jué)進(jìn)入死循環(huán)了,小數(shù)部分乘以2,一直乘不到小數(shù)部分為0
就像十進(jìn)制中1/3,結(jié)果是0.3(3...)這樣的問(wèn)題一樣,0.1轉(zhuǎn)成二進(jìn)制時(shí)也會(huì)存在精度問(wèn)題,我們需要進(jìn)行取舍。
我們看一下0.1在計(jì)算機(jī)中是怎么存儲(chǔ)的。對(duì)此,需要了解一下浮點(diǎn)數(shù)的概念。
浮點(diǎn)數(shù)
浮點(diǎn)數(shù),顧名思義,小數(shù)點(diǎn)是浮動(dòng)的數(shù)。千萬(wàn)不要以為浮點(diǎn)數(shù)就是小數(shù)。因?yàn)椋趈s中是沒(méi)有整數(shù)和小數(shù)的概念的,其實(shí)整數(shù)也是以浮點(diǎn)數(shù)的形式表示的,只是小數(shù)部分為0而已。
浮點(diǎn)數(shù)簡(jiǎn)單理解,就是類似于我們十進(jìn)制中的科學(xué)計(jì)數(shù)法。在計(jì)算機(jī)中一般遵循IEEE 754標(biāo)準(zhǔn)。格式如下:
(-1)^S * M * 2^E 1. S表示符號(hào)位,當(dāng)S=0時(shí),為正數(shù);當(dāng)S=1時(shí),為負(fù)數(shù)。 2. M表示有效數(shù)字(尾數(shù)),大于等于1,小于2。 3.
E為指數(shù)(也叫階碼)。
因此,上邊的10.25(二進(jìn)制1010.01)按照此格式表示即為 1.01001 * 2^3
對(duì)于32位浮點(diǎn)數(shù)來(lái)說(shuō),符號(hào)位占一位,指數(shù)位占8位,尾數(shù)占23位
對(duì)于64位浮點(diǎn)數(shù)來(lái)說(shuō),符號(hào)位占一位,指數(shù)位占11位,尾數(shù)占52位
IEEE 754標(biāo)準(zhǔn)
注意:IEEE
754標(biāo)準(zhǔn)規(guī)定,在保存尾數(shù)M時(shí),第一位默認(rèn)是1,因此可以被舍去,只存儲(chǔ)后邊的部分。例如,1.01001保存的時(shí)候,只保存01001,等到用的時(shí)候再把1加上去。這樣,就可以節(jié)省一個(gè)位的有效數(shù)字。
指數(shù)E在存儲(chǔ)的時(shí)候也有些特殊。若為32位,指數(shù)占8位,則可表示的大小范圍為0-255 。如為64位,指數(shù)占11位,范圍為0-2047
。但是,指數(shù)是有正有負(fù)的,因此實(shí)際值需要在此基礎(chǔ)上減去一個(gè)中間數(shù)。對(duì)于32位,中間數(shù)為127,對(duì)于64位,中間數(shù)為1023 。
還是以1.01001 * 2^3 為例,若為32位浮點(diǎn)數(shù),則需要保存成 3+ 127 = 130,即二進(jìn)制的10000010,若為64位浮點(diǎn)數(shù),則保存成
3+ 1023 = 1026 ,即二進(jìn)制的10000000010。
計(jì)算步驟
好了。巴拉巴拉了這么多。終于,要進(jìn)入我們今天的正題了。
我們看一下 0.1 在計(jì)算機(jī)中是怎么用 IEEE 754標(biāo)準(zhǔn)存儲(chǔ)的。
十進(jìn)制0.1轉(zhuǎn)為二進(jìn)制為0.0001100110011(0011循環(huán)),即
1.100110011(0011)*2^-4,因此符號(hào)位為0,尾數(shù)1.100110011(0011),階碼為 -4,實(shí)際存儲(chǔ)為 -4+1023 = 1019
的二進(jìn)制 1111111011
0 01111111011 1001100110011001100110011001100110011001100110011010 S E指數(shù) M尾數(shù)
十進(jìn)制0.2轉(zhuǎn)為二進(jìn)制為0.001100110011(0011循環(huán)),即 1.100110011(0011)*2^-3 ,存儲(chǔ)時(shí),符號(hào)位為0,尾數(shù)
1.100110011(0011),階碼為-3,實(shí)際存儲(chǔ)為 -3+1023 = 1020 的二進(jìn)制 1111111100。
0 01111111100 1001100110011001100110011001100110011001100110011010 S E指數(shù) M尾數(shù)
接下來(lái),計(jì)算 0.1 + 0.2 。
浮點(diǎn)數(shù)進(jìn)行計(jì)算時(shí),需要對(duì)階。即把兩個(gè)數(shù)的階碼設(shè)置為一樣的值,然后再計(jì)算尾數(shù)部分。其實(shí)對(duì)階很好理解,就和我們十進(jìn)制科學(xué)記數(shù)法加法一個(gè)道理,先把指數(shù)部分化成一樣,再計(jì)算尾數(shù)。
另外,需要注意一下,對(duì)階時(shí)需要小階對(duì)大階。因?yàn)?,這樣相當(dāng)于,小階指數(shù)乘以倍數(shù),尾數(shù)部分相對(duì)應(yīng)的除以倍數(shù),在二進(jìn)制中即右移倍數(shù)位。這樣,不會(huì)影響到尾數(shù)的高位,只會(huì)移出低位,損失相對(duì)較少的精度。
因此,0.1的階碼為 -4 , 需要對(duì)階為 0.2的階碼 -3 。尾數(shù)部分整體右移一位。
原來(lái)的0.1 0 01111111011 1001100110011001100110011001100110011001100110011010
對(duì)階后的0.1 0 01111111100 1100110011001100110011001100110011001100110011001101
然后進(jìn)行尾數(shù)部分相加
0 01111111100 1100110011001100110011001100110011001100110011001101 + 0
01111111100 1001100110011001100110011001100110011001100110011010 = 0
01111111100 10110011001100110011001100110011001100110011001100111
可以看到,產(chǎn)生了進(jìn)位。因此,階碼需要 +1,即為 -2,尾數(shù)部分進(jìn)行低位四舍五入處理。因尾數(shù)最低位為1,需要進(jìn)位。所以存儲(chǔ)為:
0 1111111101 1011001100110011001100110011001100110011001100110100
最后把二進(jìn)制轉(zhuǎn)換為十進(jìn)制,
二進(jìn)制為: 1.1011001100110011001100110011001100110011001100110100 * 2^-2 轉(zhuǎn)為十進(jìn)制為:
2^-2 * (1*2^0 + 1*2^-1 + 0 + 1*2^-3 + 1*2^-4 + ...) 最終結(jié)果為:
0.3000000000000000444089209850062616169452667236328125 因?yàn)榫葐?wèn)題,只取到:
0.30000000000000004
問(wèn)題總結(jié)
1.在十進(jìn)制轉(zhuǎn)換為二進(jìn)制的過(guò)程中,會(huì)產(chǎn)生精度的損失。
2.二進(jìn)制浮點(diǎn)數(shù)進(jìn)行對(duì)階運(yùn)算時(shí),也會(huì)產(chǎn)生精度的損失。
因此,最終結(jié)果才產(chǎn)生了偏差。
看完的小伙伴,現(xiàn)在應(yīng)該能理解,為什么0.1 + 0.2 ≠ 0.3 這個(gè)問(wèn)題了吧。
熱門工具 換一換
