JS精度缺失問(wèn)題

案例

小明是一位前端新人蔚袍,經(jīng)過(guò)大學(xué)期間的苦學(xué)勤練終于出山,就職某公司負(fù)責(zé)某電商平臺(tái)捣染。經(jīng)過(guò)一年多的項(xiàng)目實(shí)戰(zhàn)艳吠,小明碼代碼漸臻佳境衙四,發(fā)現(xiàn)JS也:


haixing.jpg

然而有一天,測(cè)試小力找到小明,告訴小明“你寫(xiě)的秒殺活動(dòng)價(jià)格有bug”。小明心里咯噔一下:我寫(xiě)的代碼怎么會(huì)有bug五督,是你測(cè)試姿勢(shì)有問(wèn)題吧?想歸想瓶殃,嘴里還是應(yīng)下來(lái)先看一下充包。根據(jù)小力提供的測(cè)試案例,小明很快就重現(xiàn)了問(wèn)題:價(jià)格為1.855的秒殺商品遥椿,在前端toFixed后竟然變成了1.85误证。經(jīng)過(guò)一番搜索,小明把bug給解決了修壕,也知道這是JS的精度缺失導(dǎo)致的問(wèn)題。那么遏考,問(wèn)題來(lái)了:是什么導(dǎo)致的精度缺失慈鸠?怎么解決精度缺失問(wèn)題?

  • 常見(jiàn)的JS精度缺失場(chǎng)景:
1.855.toFixed(2) // 1.85
0.1 + 0.2  // 0.30000000000000004
9999999999999999 == 10000000000000001 // true
什么導(dǎo)致的精度缺失

在JavaScript中灌具,Number類型是采用IEEE754二進(jìn)制浮點(diǎn)數(shù)算術(shù)標(biāo)準(zhǔn)雙精度64位浮點(diǎn)數(shù)來(lái)存儲(chǔ)的青团,運(yùn)算也是基于二進(jìn)制來(lái)運(yùn)算,最終才轉(zhuǎn)換成十進(jìn)制的結(jié)果咖楣。由于位數(shù)限制(64bit)督笆,一些不能有窮表示的二進(jìn)制數(shù)只能是真正值的近似,經(jīng)過(guò)運(yùn)算后偏差疊加诱贿,會(huì)出現(xiàn)一些意料之外的結(jié)果娃肿,也就是我們所說(shuō)的精度缺失咕缎。

  • 64bit組成:
sign exponent fraction
符號(hào)位 指數(shù)域 尾數(shù)域
第1位 2~12位 13~64位
轉(zhuǎn)換二進(jìn)制

以上面0.1與0.2為例,在十進(jìn)制里面可以有窮表示料扰,但是轉(zhuǎn)換成二進(jìn)制后凭豪,它們就是無(wú)窮的。具體轉(zhuǎn)換方式可以遵循“正整數(shù)除二取余晒杈,小數(shù)乘二取整”的方式:

// 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
0.4*2=0.8****************取出整數(shù)部分0
0.8*2=1.6****************取出整數(shù)部分1
0.6*2=1.2****************取出整數(shù)部分1
.
.
.
// 結(jié)果 0.1D = 00011001100110011...B
// 科學(xué)計(jì)數(shù)法表示: 2^-4*1.100110011...B
//  尾數(shù)域52 位嫂伞,超過(guò)部分進(jìn)一舍零:2^-4*1.10011(0011 repeat 12 times)010B
最后結(jié)果:2^-4*1.1001100110011001100110011001100110011001100110011010B

同理可以得出0.2的二進(jìn)制:

0.2D = 2^-3*1.1001100110011001100110011001100110011001100110011010B

0.1 + 0.2:

  2^-4*1.1001100110011001100110011001100110011001100110011010B
+ 2^-3*1.1001100110011001100110011001100110011001100110011010B
相加時(shí)指數(shù)不一致,需要對(duì)齊:
  0.1100110011001100110011001100110011001100110011001101B
+ 1.1001100110011001100110011001100110011001100110011010B
--------------------------------------------------------------
=10.0110011001100110011001100110011001100110011001100111B

得到的二進(jìn)制結(jié)果為:10.0110011001100110011001100110011001100110011001100111B
重新用科學(xué)計(jì)數(shù)法表示:2^-2*1.0011001100110011001100110011001100110011001100110100
最終轉(zhuǎn)化為十進(jìn)制的數(shù)值:

let b = '010011001100110011001100110011001100110011001100110100';
let num = 0;
for (let i = 0, len = b.length; i < len; i++) {
    num += b[i] * Math.pow(2, -i-1);
}
num; // 0.30000000000000004
精度缺失解決

這里介紹一個(gè)第三方庫(kù)decimal.js拯钻,項(xiàng)目里面可以進(jìn)行二次封裝使用帖努。

彩蛋

ES6 在Number對(duì)象上新增了一個(gè)極小的常量Number.EPSILON,表示JavaScript的最小精度粪般,誤差如果小于這個(gè)值拼余,就可以把二者看成是相等的。例如:

0.1 + 0.2 === 0.3 // false
0.1 + 0.2 - 0.3 < Number.EPSILON // true

然而這個(gè)也不是無(wú)往而不利刊驴,1.1 + 1.3 - 2.4 < Number.EPSILON返回的結(jié)果再次出乎意料姿搜,是false±υ鳎看到這里舅柜,小明默默把簡(jiǎn)歷里的“精通JS”改成了“了解”。躲惰。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末致份,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子础拨,更是在濱河造成了極大的恐慌氮块,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,122評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件诡宗,死亡現(xiàn)場(chǎng)離奇詭異滔蝉,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)塔沃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門蝠引,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人蛀柴,你說(shuō)我怎么就攤上這事螃概。” “怎么了鸽疾?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,491評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵吊洼,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我制肮,道長(zhǎng)冒窍,這世上最難降的妖魔是什么递沪? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,636評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮超燃,結(jié)果婚禮上区拳,老公的妹妹穿的比我還像新娘。我一直安慰自己意乓,他們只是感情好樱调,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著届良,像睡著了一般笆凌。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上士葫,一...
    開(kāi)封第一講書(shū)人閱讀 51,541評(píng)論 1 305
  • 那天乞而,我揣著相機(jī)與錄音,去河邊找鬼慢显。 笑死爪模,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的荚藻。 我是一名探鬼主播屋灌,決...
    沈念sama閱讀 40,292評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼应狱!你這毒婦竟也來(lái)了共郭?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,211評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤疾呻,失蹤者是張志新(化名)和其女友劉穎除嘹,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體岸蜗,經(jīng)...
    沈念sama閱讀 45,655評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡尉咕,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了璃岳。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片龙考。...
    茶點(diǎn)故事閱讀 39,965評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖矾睦,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情炎功,我是刑警寧澤枚冗,帶...
    沈念sama閱讀 35,684評(píng)論 5 347
  • 正文 年R本政府宣布,位于F島的核電站蛇损,受9級(jí)特大地震影響赁温,放射性物質(zhì)發(fā)生泄漏坛怪。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評(píng)論 3 329
  • 文/蒙蒙 一股囊、第九天 我趴在偏房一處隱蔽的房頂上張望袜匿。 院中可真熱鬧,春花似錦稚疹、人聲如沸居灯。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,894評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)怪嫌。三九已至,卻和暖如春柳沙,著一層夾襖步出監(jiān)牢的瞬間岩灭,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,012評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工赂鲤, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留噪径,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,126評(píng)論 3 370
  • 正文 我出身青樓数初,卻偏偏與公主長(zhǎng)得像找爱,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子妙真,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評(píng)論 2 355