2018-08-02 0.1+0.2=0.30000000000000004

最近偶然發(fā)現(xiàn)了0.1+0.2 = 0.30000000000000004的現(xiàn)實(shí),當(dāng)然類似于此的還有很多砰粹,比如0.7*n結(jié)果很多都是一個無限小數(shù)庐扫,本來以為這是千萬個js的坑之一炼杖,但是后來發(fā)現(xiàn)很多語言都有這個問題泳桦,這個問題并不是js的機(jī)制所導(dǎo)致的蜗字,而是所有語言的浮點(diǎn)數(shù)標(biāo)準(zhǔn)IEEE 754所導(dǎo)致的打肝,所以,這篇文章想要大致分享一下探索過程以及究竟為什么會出現(xiàn)這樣的情況挪捕。

這篇文章的主題就是:垃圾浮點(diǎn)數(shù)標(biāo)準(zhǔn)粗梭,自帶誤差,所以算出來不對不能怪電腦<读恪6弦健(好了完了,誤)其實(shí)看了好久這個奏纪,本以為一下午就可以解決的事情鉴嗤,折騰了幾天的感覺(當(dāng)然主要也有工作穿插的原因嘛),最終雖然模模糊糊理解了大致的原因序调,但是不知道自己能不能真正的解釋清楚醉锅。嗯嗯,當(dāng)然浮點(diǎn)數(shù)不是垃圾发绢,IEEE 754標(biāo)準(zhǔn)也是集結(jié)了前人智慧的硬耍,下面進(jìn)入正題。

分析這個問題边酒,其實(shí)需要了解原碼经柴,反碼,補(bǔ)碼等概念墩朦,然后會根據(jù)這些數(shù)碼進(jìn)行二進(jìn)制的運(yùn)算坯认,然后再從這些部分延申到二進(jìn)制浮點(diǎn)數(shù),再逐步分析浮點(diǎn)數(shù)的加減。但是這樣其實(shí)有點(diǎn)繁瑣牛哺,整理起來也沒那么簡單陋气,所以我們首先聚焦這個問題:0.1+0.2的結(jié)果按道理來說是0.3,但是為什么這里會有溢出呢荆隘?我們剛才也說到了IEEE 754標(biāo)準(zhǔn),那么我關(guān)注的就是這個標(biāo)準(zhǔn)赴背,浮點(diǎn)數(shù)在這個標(biāo)準(zhǔn)之下是怎樣進(jìn)行存儲的椰拒,導(dǎo)致了有溢出出現(xiàn)呢?

我們都知道凰荚,在計(jì)算機(jī)之中燃观,所有的數(shù)據(jù)都是二進(jìn)制存儲的,比如說數(shù)字5便瑟,二進(jìn)制表示為101缆毁,數(shù)字15,二進(jìn)制表示為1111到涂。而且在計(jì)算機(jī)中,數(shù)字的表示還是有一定格式的践啄,也就是精度。比如屿讽,有數(shù)據(jù)類型的限制是用8位表示,而且又要分正負(fù)伐谈,那么它的取值范圍就是-2^7 ~ 2^7-1(c++中的char類型烂完,就是這樣的取值范圍,有人可能會疑惑為什么不是-128 ~ 128诵棵,這個稍后再講)。所以如果用這樣的類型表示數(shù)字履澳,5就是00000101,15就是00001111奇昙,采用高位用0補(bǔ)齊的方式存儲到內(nèi)存當(dāng)中护侮。

那浮點(diǎn)數(shù)怎么辦呢?諸如20.4储耐,30.67羊初,0.5等等或簡單或復(fù)雜的小數(shù),我們首先發(fā)現(xiàn)的問題是小數(shù)位該怎么去表示长赞。我們在中學(xué)中有學(xué)到整數(shù)的表示法可以稱為“除2取余法”,比如5/2 = 2 ……1得哆,2/2 = 1……0,1/2 = 0 …… 1贩据,所以5表示為101,這么做的原因就是5可以表示為12^2+021+1*20矾芙。那么我們反過來思考小數(shù),是不是也可以表示成2e相加(e為負(fù)數(shù))的形式剔宪,但是遺憾的是,2-1 = 0.5葱绒, 2^-2 = 0.25斗锭,不像2的正數(shù)次方那么有規(guī)律哈街,所以注定有些數(shù)我們是表示不了的拒迅。但是作為一個完整的運(yùn)算系統(tǒng),這些數(shù)字我們是不可能舍棄的呀璧微,所以我們只能近似的取到這些數(shù)字。

等等胞得,我們好像還沒說浮點(diǎn)數(shù)該怎么表示屹电,怎么好像已經(jīng)發(fā)現(xiàn)了浮點(diǎn)數(shù)會出問題的原因了阶剑。其實(shí)只想知道大致為什么的同志危号,到這里就可以over了,其核心原因就是外莲,本身用二進(jìn)制來表示小數(shù)就會難以覆蓋猪半,所以采用了一種近似的方式兔朦,既然是近似,那么有誤差的出現(xiàn)磨确,似乎也沒那么奇怪了。下面將結(jié)合我的了解和資料的查詢分析乏奥,探索一下究竟是怎樣的二進(jìn)制存儲與運(yùn)算才導(dǎo)致了這樣的情形。

首先就是剛才還未說完的二進(jìn)制表示浮點(diǎn)數(shù)恨诱,對于小數(shù)部分驶悟,我們需要使用“乘2取整法”材失,例如0.875
0.875*2 = 1.75 整數(shù)部分 1
0.75 * 2 = 1.5 整數(shù)部分 1
0.5 * 2 = 1.0 整數(shù)部分 1

所以0.875的二進(jìn)制的小數(shù)部分表示就是 111,我們逆向計(jì)算一下龙巨,12^-1 + 12^-2 + 1*2^-3 = 0.875
從理論數(shù)據(jù)上我們看到,這樣做是沒錯的旨别,雖然我們選擇的數(shù)據(jù)可能有點(diǎn)那么“正正好”。

那么接下來我們就碰到了下一個嚴(yán)峻的問題铭若,小數(shù)點(diǎn)怎么辦递览?二進(jìn)制是沒有辦法表示小數(shù)點(diǎn)的呀,那就輪到我們的IEEE 754登場了绞铃。在計(jì)算機(jī)中,浮點(diǎn)數(shù)(此處以單精度float32位為例荚坞,當(dāng)然js使用的也是這一個菲盾,在c++等語言中還有雙精度double64位颓影,這個位就是剛才我所說的精度的概念)采用了一種特別的方式去保存懒鉴,在涉及到小數(shù)位的時候,你需要先把小數(shù)轉(zhuǎn)換為二進(jìn)制向上面那樣咆畏,0.875轉(zhuǎn)換成了0.111,然后通過移位讓數(shù)字的整數(shù)部分為1旧找,形成1.xxxxx * 2e的形式,所以0.111就可以表示成1.11*2-1钮蛛。在IEEE 754之中,浮點(diǎn)數(shù)的存儲分為三個部分岭辣,在各種文獻(xiàn)中的解釋極其正規(guī)的解釋了三部分叫做甸饱,sign bit,exponent bias叹话,fraction,emmm大致是如下結(jié)構(gòu)

image.png

他們各自的命名是符號為氏豌,偏移量(移碼热凹,階碼)

注:
原碼是一個數(shù)的二進(jìn)制,而反碼是這個數(shù)對于當(dāng)前數(shù)位的滿值的補(bǔ)值般妙。啊,這句話說的我自己都不理解什么意思霹陡,舉個例子(一下用四位進(jìn)行表示止状,一位符號位烹棉,三位真值域):

a = 2 (0010)
a取反怯疤,a的反碼0101,記作b=5
a+b = 0111 = 7伏社,為當(dāng)前數(shù)位的滿值,即2^n-1
這個道理其實(shí)恰恰印證了模運(yùn)算的合理性與應(yīng)用在二進(jìn)制運(yùn)算上的正確性

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末速妖,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子罕容,更是在濱河造成了極大的恐慌稿饰,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,273評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件旅择,死亡現(xiàn)場離奇詭異侣姆,居然都是意外死亡生真,警方通過查閱死者的電腦和手機(jī)铺敌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評論 3 398
  • 文/潘曉璐 我一進(jìn)店門偿凭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人弯囊,你說我怎么就攤上這事胶果。” “怎么了早抠?”我有些...
    開封第一講書人閱讀 167,709評論 0 360
  • 文/不壞的土叔 我叫張陵蕊连,是天一觀的道長悬垃。 經(jīng)常有香客問我甘苍,道長,這世上最難降的妖魔是什么看彼? 我笑而不...
    開封第一講書人閱讀 59,520評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮靖榕,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘鸯绿。我一直安慰自己,他們只是感情好瓶蝴,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,515評論 6 397
  • 文/花漫 我一把揭開白布租幕。 她就那樣靜靜地躺著,像睡著了一般男窟。 火紅的嫁衣襯著肌膚如雪贾富。 梳的紋絲不亂的頭發(fā)上歉眷,一...
    開封第一講書人閱讀 52,158評論 1 308
  • 那天汗捡,我揣著相機(jī)與錄音,去河邊找鬼扇住。 笑死盗胀,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的票灰。 我是一名探鬼主播,決...
    沈念sama閱讀 40,755評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼浸策,長吁一口氣:“原來是場噩夢啊……” “哼屈糊!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起逻锐,我...
    開封第一講書人閱讀 39,660評論 0 276
  • 序言:老撾萬榮一對情侶失蹤雕薪,失蹤者是張志新(化名)和其女友劉穎晓淀,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體凶掰,經(jīng)...
    沈念sama閱讀 46,203評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡懦窘,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,287評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了畅涂。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,427評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡立宜,死狀恐怖臊岸,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情帅戒,我是刑警寧澤,帶...
    沈念sama閱讀 36,122評論 5 349
  • 正文 年R本政府宣布施流,位于F島的核電站鄙信,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏装诡。R本人自食惡果不足惜践盼,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,801評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望咕幻。 院中可真熱鬧,春花似錦锣吼、人聲如沸选浑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,272評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽隧膘。三九已至寺惫,卻和暖如春疹吃,著一層夾襖步出監(jiān)牢的瞬間西雀,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評論 1 272
  • 我被黑心中介騙來泰國打工篡撵, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留豆挽,地道東北人育谬。 一個月前我還...
    沈念sama閱讀 48,808評論 3 376
  • 正文 我出身青樓膛檀,卻偏偏與公主長得像,于是被迫代替她去往敵國和親咖刃。 傳聞我的和親對象是個殘疾皇子憾筏,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,440評論 2 359

推薦閱讀更多精彩內(nèi)容