為什么0.1+0.2=0.30000000000000004

?閱讀本文大約需要8分鐘...

問題

在計(jì)算機(jī)的世界里,可能有很多常人無法理解的事情劣挫。比如 0.1 + 0.2 = ?东帅。來压固,告訴我你的答案。

有的朋友看到這就迫不及待的說靠闭,這么簡單的問題帐我,很明顯等于 0.3 啊,小學(xué)生都會(huì)算的好伐阎毅。你這是在侮辱我的智商焚刚?

好吧,我來告訴你一個(gè)打臉的事實(shí)扇调,0.1 + 0.2 還真不等于 0.3 。先別急著反駁我抢肛。

打開你的任意一個(gè)瀏覽器(我用chrome做演示)狼钮,F(xiàn)12打開console控制臺(tái),輸入 console.log(0.1 + 0.2) 捡絮。如果你操作正確的話熬芜,你會(huì)看到以下的結(jié)果。

file

是不是感覺匪夷所思福稳,what涎拉?為什么結(jié)果是0.30000000000000004,這是神魔鬼的圆? 難道鼓拧,我這么多年學(xué)習(xí)的數(shù)學(xué)知識(shí),老師教的都是錯(cuò)的越妈?

別著急季俩。其實(shí),你的老師教的沒錯(cuò)梅掠,在我們的世界中酌住,0.1 + 0.2 確實(shí)是等于 0.3 的店归。但是,在計(jì)算機(jī)中酪我,可就不是這么一回事了消痛。待我娓娓道來。

因?yàn)槎伎蓿覀冊谟?jì)算數(shù)學(xué)問題的時(shí)候秩伞,用的是十進(jìn)制,計(jì)算出來結(jié)果是0.3沒問題质涛。但是稠歉,在計(jì)算機(jī)中用的是二進(jìn)制,都是由0和1來組成汇陆。這就不得不提一下怒炸,十進(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

等等盟步,怎么感覺進(jìn)入死循環(huán)了藏斩,小數(shù)部分乘以2,一直乘不到小數(shù)部分為0

就像十進(jìn)制中1/3却盘,結(jié)果是0.3(3...)這樣的問題一樣狰域,0.1轉(zhuǎn)成二進(jìn)制時(shí)也會(huì)存在精度問題,我們需要進(jìn)行取舍黄橘。

我們看一下0.1在計(jì)算機(jī)中是怎么存儲(chǔ)的兆览。對此,需要了解一下浮點(diǎn)數(shù)的概念旬陡。

浮點(diǎn)數(shù)

浮點(diǎn)數(shù)拓颓,顧名思義,小數(shù)點(diǎn)是浮動(dòng)的數(shù)描孟。千萬不要以為浮點(diǎn)數(shù)就是小數(shù)驶睦。因?yàn)榕樽螅趈s中是沒有整數(shù)和小數(shù)的概念的,其實(shí)整數(shù)也是以浮點(diǎn)數(shù)的形式表示的场航,只是小數(shù)部分為0而已缠导。

浮點(diǎn)數(shù)簡單理解,就是類似于我們十進(jìn)制中的科學(xué)計(jì)數(shù)法溉痢。在計(jì)算機(jī)中一般遵循IEEE 754標(biāo)準(zhǔn)僻造。格式如下:

(-1)^S * M * 2^E
1. S表示符號位,當(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

對于32位浮點(diǎn)數(shù)來說轩娶,符號位占一位儿奶,指數(shù)位占8位,尾數(shù)占23位
對于64位浮點(diǎn)數(shù)來說鳄抒,符號位占一位闯捎,指數(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ù)。對于32位勺馆,中間數(shù)為127戏售,對于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,因此符號位為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í),符號位為0颤诀,尾數(shù) 1.100110011(0011)字旭,階碼為-3,實(shí)際存儲(chǔ)為 -3+1023 = 1020 的二進(jìn)制 1111111100崖叫。

0  01111111100  1001100110011001100110011001100110011001100110011010
S     E指數(shù)          M尾數(shù)

接下來遗淳,計(jì)算 0.1 + 0.2 。

浮點(diǎn)數(shù)進(jìn)行計(jì)算時(shí)心傀,需要對階屈暗。即把兩個(gè)數(shù)的階碼設(shè)置為一樣的值,然后再計(jì)算尾數(shù)部分。其實(shí)對階很好理解养叛,就和我們十進(jìn)制科學(xué)記數(shù)法加法一個(gè)道理种呐,先把指數(shù)部分化成一樣,再計(jì)算尾數(shù)一铅。

另外陕贮,需要注意一下,對階時(shí)需要小階對大階潘飘。因?yàn)榘怪@樣相當(dāng)于,小階指數(shù)乘以倍數(shù)卜录,尾數(shù)部分相對應(yīng)的除以倍數(shù)戈擒,在二進(jìn)制中即右移倍數(shù)位。這樣艰毒,不會(huì)影響到尾數(shù)的高位筐高,只會(huì)移出低位,損失相對較少的精度丑瞧。

因此柑土,0.1的階碼為 -4 , 需要對階為 0.2的階碼 -3 。尾數(shù)部分整體右移一位绊汹。

原來的0.1
0  01111111011  1001100110011001100110011001100110011001100110011010
對階后的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)榫葐栴}庵楷,只取到:
0.30000000000000004

問題總結(jié)

1.在十進(jìn)制轉(zhuǎn)換為二進(jìn)制的過程中,會(huì)產(chǎn)生精度的損失楣颠。

2.二進(jìn)制浮點(diǎn)數(shù)進(jìn)行對階運(yùn)算時(shí)嫁乘,也會(huì)產(chǎn)生精度的損失。

因此球碉,最終結(jié)果才產(chǎn)生了偏差。

看完的小伙伴仓蛆,現(xiàn)在應(yīng)該能理解睁冬,為什么0.1 + 0.2 ≠ 0.3 這個(gè)問題了吧。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市豆拨,隨后出現(xiàn)的幾起案子直奋,更是在濱河造成了極大的恐慌,老刑警劉巖施禾,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件脚线,死亡現(xiàn)場離奇詭異,居然都是意外死亡弥搞,警方通過查閱死者的電腦和手機(jī)邮绿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來攀例,“玉大人船逮,你說我怎么就攤上這事≡撩” “怎么了挖胃?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長梆惯。 經(jīng)常有香客問我酱鸭,道長,這世上最難降的妖魔是什么垛吗? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任凹髓,我火速辦了婚禮,結(jié)果婚禮上职烧,老公的妹妹穿的比我還像新娘扁誓。我一直安慰自己,他們只是感情好蚀之,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布蝗敢。 她就那樣靜靜地躺著,像睡著了一般足删。 火紅的嫁衣襯著肌膚如雪寿谴。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天失受,我揣著相機(jī)與錄音讶泰,去河邊找鬼。 笑死拂到,一個(gè)胖子當(dāng)著我的面吹牛痪署,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播兄旬,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼狼犯,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起悯森,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤宋舷,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后瓢姻,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體祝蝠,經(jīng)...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年幻碱,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了绎狭。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,622評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡收班,死狀恐怖坟岔,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情摔桦,我是刑警寧澤社付,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站邻耕,受9級特大地震影響鸥咖,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜兄世,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一啼辣、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧御滩,春花似錦鸥拧、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至氛驮,卻和暖如春腕柜,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背矫废。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工盏缤, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蓖扑。 一個(gè)月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓唉铜,卻偏偏與公主長得像,于是被迫代替她去往敵國和親律杠。 傳聞我的和親對象是個(gè)殘疾皇子打毛,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評論 2 348