浮點(diǎn)數(shù)加法運(yùn)算的困惑

緣起:

公司一小姑娘群發(fā)郵件求助毁葱,郵件內(nèi)容是關(guān)于浮點(diǎn)數(shù)之間運(yùn)算結(jié)果莫名其妙的多了一些小數(shù)位奕坟,小姑娘想不通所以想問(wèn)問(wèn)有人知道原因不距潘?涉及的代碼是下面的樣子

function addTwoFloatString(stra , strb){
    return parseFloat(stra) + parseFloat(strb);
}

addTwoFloatString('2222222','269567.53');
->2491789.5300000003

大家看到這個(gè)問(wèn)題后獻(xiàn)計(jì)獻(xiàn)策甜熔,有說(shuō)不應(yīng)該那樣蜓氨,應(yīng)該這樣

Number( 123.5678.toFixed(2) )
還有說(shuō)應(yīng)該用BigDecimal做浮點(diǎn)數(shù)相加運(yùn)算的聋袋。

看到這個(gè)求助后,直覺(jué)告訴我這個(gè)和浮點(diǎn)數(shù)的表示法肯定有關(guān)系穴吹,至于為什么幽勒,詳細(xì)的答案是真說(shuō)不好。如果非要搪塞一下港令,我會(huì)說(shuō)浮點(diǎn)數(shù)之間的運(yùn)算會(huì)有精度問(wèn)題啥容,如果深究為什么,那就糗了顷霹。

驗(yàn)證

首先看看是否其他語(yǔ)言中也存在類(lèi)似的問(wèn)題咪惠,ok,java中也存在同樣的問(wèn)題淋淀,那么基本可以斷定這個(gè)現(xiàn)象和編程語(yǔ)言關(guān)系不大遥昧。

接著需要確認(rèn)的問(wèn)題是,JavaScript中浮點(diǎn)數(shù)是如何定義的:

JavaScript中的浮點(diǎn)數(shù)采用IEEE-754格式的規(guī)定。更具體的說(shuō)是一個(gè)雙精度格式炭臭,這意味著每個(gè)浮點(diǎn)數(shù)占64位叫乌。雖然它不是二進(jìn)制表示浮點(diǎn)數(shù)的唯一途徑,但它是目前最廣泛使用的格式徽缚,另外憨奸,JavaScript中的數(shù)值不區(qū)分整數(shù)值和浮點(diǎn)數(shù)值,所有數(shù)字都采用的是64位浮點(diǎn)數(shù)表示法凿试。

似乎看到了希望排宰,IEEE754

wiki中對(duì)IEEE754中64位浮點(diǎn)數(shù)有下面的描述,總長(zhǎng)度占用64位那婉,其中1為符號(hào)位板甘,標(biāo)明浮點(diǎn)數(shù)是正數(shù)還是負(fù)數(shù),11位為指數(shù)為详炬,52位為尾數(shù)位盐类。

知道這些現(xiàn)在還不能準(zhǔn)確的表述一個(gè)數(shù),得有一個(gè)約定呛谜,約定尾數(shù)的形式在跳,不然同一個(gè)數(shù)字就有多種表示,這個(gè)過(guò)程稱為規(guī)格化隐岛,形象化的描述就是一個(gè)數(shù)需要表示成這樣的格式: 1.M* 2E猫妙,這樣的數(shù)叫做規(guī)格化數(shù)。

另外聚凹,IEEE還規(guī)定了指數(shù)的存儲(chǔ)形式割坠,64位浮點(diǎn)數(shù)指數(shù)部分以偏正值形式表示,偏正值為實(shí)際的指數(shù)大小與1023的和妒牙。

到此彼哼,就可以將文章開(kāi)頭的兩個(gè)數(shù)用IEEE754的定義表示出來(lái)。

對(duì)2222222做規(guī)格化湘今, 符號(hào)位為0敢朱,表示是一個(gè)正數(shù),2222222對(duì)應(yīng)的二進(jìn)制是1000011110100010001110象浑,有22位蔫饰,滿足規(guī)格化需小數(shù)點(diǎn)左移21為,指數(shù)就為21 +1023 = 1044愉豺,尾數(shù)為1.000011110100010001110

最終2222222 的64位二進(jìn)制浮點(diǎn)數(shù)就表示為

0 10000010100 0000111101000100011100000000000000000000000000000000

269567.53的64位浮點(diǎn)數(shù)就表示為

0 10000010001 0000011100111111111000011110101110000101000111101100

Java中有對(duì)應(yīng)的函數(shù)可以將根據(jù)IEEE 754規(guī)定篓吁,返回指定浮點(diǎn)值的表示形式

Long.toBinaryString(Double.doubleToRawLongBits(2222222))

接下來(lái)就是浮點(diǎn)數(shù)加減法運(yùn)算步驟了,如下

  1. 對(duì)階蚪拦,使兩個(gè)數(shù)的小數(shù)點(diǎn)位置對(duì)齊

  2. 尾數(shù)求和杖剪,將運(yùn)算后的兩個(gè)尾數(shù)求和

  3. 將求和后的尾數(shù)規(guī)格化

  4. 舍入冻押,考慮尾數(shù)右移時(shí)的丟失的數(shù)值位

  5. 判斷結(jié)果是否溢出

  • 對(duì)階的原則是小階向大階看齊,階碼較小的數(shù)尾數(shù)向右移盛嘿,每移一位洛巢,階碼加一,在269567.53 + 2222222中次兆,前者向后者看齊稿茉,前者尾數(shù)右移三位。對(duì)階后兩數(shù)分別為

     010000010100 0010000011100111111111000011110101110000101000111101100
    
     010000010100 0000111101000100011100000000000000000000000000000000
    
  • 尾數(shù)相加

     0100000101000010000011100111111111000011110101110000101000111101100
    
     0100000101000000111101000100011100000000000000000000000000000000
    
     0100000101000011000000101100011011000011110101110000101000111111100
    
  • 規(guī)格化

滿足規(guī)格化

  • 舍入

      0100000101000011000000101100011011000011110101110000101000111111100
    

浮點(diǎn)數(shù)舍入的方式有多種芥炭,但是不可避免的漓库,都可能會(huì)發(fā)生數(shù)位的丟失,這個(gè)就是直接導(dǎo)致浮點(diǎn)數(shù)之間運(yùn)算后出現(xiàn)精度問(wèn)題的直接原因园蝠。

java和javascript中最終舍入后的表示形式為

0100000101000011000000101100011011000011110101110000101000111110

結(jié)論

由于二進(jìn)制表示本身的缺陷渺蒿,我們不得不面對(duì)一個(gè)充滿舍入誤差的規(guī)范,進(jìn)而導(dǎo)致計(jì)算結(jié)果超乎預(yù)期彪薛。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末茂装,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子善延,更是在濱河造成了極大的恐慌少态,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,348評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件挚冤,死亡現(xiàn)場(chǎng)離奇詭異况增,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)训挡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,122評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)歧强,“玉大人澜薄,你說(shuō)我怎么就攤上這事√幔” “怎么了肤京?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,936評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)茅特。 經(jīng)常有香客問(wèn)我忘分,道長(zhǎng),這世上最難降的妖魔是什么白修? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,427評(píng)論 1 283
  • 正文 為了忘掉前任妒峦,我火速辦了婚禮,結(jié)果婚禮上兵睛,老公的妹妹穿的比我還像新娘肯骇。我一直安慰自己窥浪,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,467評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布笛丙。 她就那樣靜靜地躺著漾脂,像睡著了一般。 火紅的嫁衣襯著肌膚如雪胚鸯。 梳的紋絲不亂的頭發(fā)上骨稿,一...
    開(kāi)封第一講書(shū)人閱讀 49,785評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音姜钳,去河邊找鬼啊终。 笑死,一個(gè)胖子當(dāng)著我的面吹牛傲须,可吹牛的內(nèi)容都是我干的蓝牲。 我是一名探鬼主播,決...
    沈念sama閱讀 38,931評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼泰讽,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼例衍!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起已卸,我...
    開(kāi)封第一講書(shū)人閱讀 37,696評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤佛玄,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后累澡,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體梦抢,經(jīng)...
    沈念sama閱讀 44,141評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,483評(píng)論 2 327
  • 正文 我和宋清朗相戀三年愧哟,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了奥吩。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,625評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蕊梧,死狀恐怖霞赫,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情肥矢,我是刑警寧澤端衰,帶...
    沈念sama閱讀 34,291評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站甘改,受9級(jí)特大地震影響旅东,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜十艾,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,892評(píng)論 3 312
  • 文/蒙蒙 一抵代、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧疟羹,春花似錦主守、人聲如沸禀倔。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)救湖。三九已至,卻和暖如春涎才,著一層夾襖步出監(jiān)牢的瞬間鞋既,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工耍铜, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留邑闺,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,324評(píng)論 2 360
  • 正文 我出身青樓棕兼,卻偏偏與公主長(zhǎng)得像陡舅,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子伴挚,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,492評(píng)論 2 348

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