摘要:又如,對(duì)于,結(jié)果其實(shí)并不是,但是最接近真實(shí)結(jié)果的數(shù),比其它任何浮點(diǎn)數(shù)都更接近。許多語(yǔ)言也就直接顯示結(jié)果為了,而不展示一個(gè)浮點(diǎn)數(shù)的真實(shí)結(jié)果了。小結(jié)本文主要介紹了浮點(diǎn)數(shù)計(jì)算問(wèn)題,簡(jiǎn)單回答了為什么以及怎么辦兩個(gè)問(wèn)題為什么不等于。
原文地址:為什么0.1+0.2不等于0.3
先看兩個(gè)簡(jiǎn)單但詭異的代碼:
0.1 + 0.2 > 0.3 // true 0.1 * 0.1 = 0.010000000000000002
0.1加0.2為什么就不等于0.3昵?要回答這個(gè)問(wèn)題,得先了解計(jì)算機(jī)內(nèi)部是如何表示數(shù)的。
計(jì)算機(jī)內(nèi)部如何表示數(shù)我們都知道,計(jì)算機(jī)用位來(lái)儲(chǔ)存及處理數(shù)據(jù)。每一個(gè)二進(jìn)制數(shù)(二進(jìn)制串)都一一對(duì)應(yīng)一個(gè)十進(jìn)制數(shù)。
這里以十進(jìn)制數(shù)13來(lái)展示“按位計(jì)數(shù)法”如何表示整數(shù):
十進(jìn)制值 | 進(jìn)制 | 按位格式 | 描述 |
---|---|---|---|
13 | 10 | 13 | 1x10^1 + 3x10^0 = 10 + 3 |
13 | 2 | 1101 | 1x2^3 + 1x2^2 + 0x2^1 + 1x2^0 = 8 + 4 + 0 + 1 |
再看小數(shù)怎么用按位計(jì)數(shù)法表示,以十進(jìn)制數(shù)0.625為例:
十進(jìn)制值 | 進(jìn)制 | 按位格式 | 描述 |
---|---|---|---|
0.625 | 10 | 0.625 | 6x10^-1 + 2x10^-2 + 5x10^-3 = 0.6 + 0.02 + 0.005 |
0.625 | 2 | 0.101 | 1x2^-1 + 0 x2^-2 + 1x2^-3 = 1/2 + 0 + 1/8 |
關(guān)于十進(jìn)制與二進(jìn)制間如何轉(zhuǎn)換,這里不細(xì)說(shuō),直接給出結(jié)論:
十進(jìn)制整數(shù)轉(zhuǎn)二進(jìn)制方法:除2取余;十進(jìn)制小數(shù)轉(zhuǎn)二進(jìn)制方法:乘2除整
十進(jìn)制0.1轉(zhuǎn)換成二進(jìn)制,乘2取整過(guò)程:
0.1 * 2 = 0.2 # 0 0.2 * 2 = 0.4 # 0 0.4 * 2 = 0.8 # 0 0.8 * 2 = 1.6 # 1 0.6 * 2 = 1.2 # 1 0.2 * 2 = 0.4 # 0 .....
從上面可以看出,0.1的二進(jìn)制格式是:0.0001100011....。這是一個(gè)二進(jìn)制無(wú)限循環(huán)小數(shù),但計(jì)算機(jī)內(nèi)存有限,我們不能用儲(chǔ)存所有的小數(shù)位數(shù)。那么在精度與內(nèi)存間如何取舍呢?
答案是:在某個(gè)精度點(diǎn)直接舍棄。當(dāng)然,代價(jià)就是,0.1在計(jì)算機(jī)內(nèi)部根本就不是精確的0.1,而是一個(gè)有舍入誤差的0.1。當(dāng)代碼被編譯或解釋后,0.1已經(jīng)被四舍五入成一個(gè)與之很接近的計(jì)算機(jī)內(nèi)部數(shù)字,以至于計(jì)算還沒(méi)開(kāi)始,一個(gè)很小的舍入錯(cuò)誤就已經(jīng)產(chǎn)生了。這也就是 0.1 + 0.2 不等于0.3 的原因。
有誤差的兩個(gè)數(shù),其計(jì)算的結(jié)果,當(dāng)然就很可能與我們期望的不一樣了。注意前面的這句話中的“很可能”這三個(gè)字?為啥是很可能昵?
0.1 + 0.1 為什么等于0.2答案是:兩個(gè)有舍入誤差的值在求和時(shí),相互抵消了,但這種“負(fù)負(fù)得正,相互抵消”不一定是可靠的,當(dāng)這兩個(gè)數(shù)字是用不同長(zhǎng)度數(shù)位來(lái)表示的浮點(diǎn)數(shù)時(shí),舍入誤差可能不會(huì)相互抵消。
又如,對(duì)于 0.1 + 0.3 ,結(jié)果其實(shí)并不是0.4,但0.4是最接近真實(shí)結(jié)果的數(shù),比其它任何浮點(diǎn)數(shù)都更接近。許多語(yǔ)言也就直接顯示結(jié)果為0.4了,而不展示一個(gè)浮點(diǎn)數(shù)的真實(shí)結(jié)果了。
另外要注意,二進(jìn)制能精確地表示位數(shù)有限且分母是2的倍數(shù)的小數(shù),比如0.5,0.5在計(jì)算機(jī)內(nèi)部就沒(méi)有舍入誤差。所以0.5 + 0.5 === 1
計(jì)算機(jī)這樣胡亂舍入,能滿足所有的計(jì)算需求嗎我們看兩個(gè)現(xiàn)實(shí)的場(chǎng)景:
對(duì)于一個(gè)修建鐵路的工程師而言,10米寬,還是10.0001米寬并沒(méi)有什么不同。鐵路工程師就不需要這么高0.x這樣的精度
對(duì)于芯片設(shè)計(jì)師,0.0001米就會(huì)是一個(gè)巨大不同,他也永遠(yuǎn)不用處理超過(guò)0.1米距離
不同行業(yè),要求的精度不是線性的,我們?cè)试S(對(duì)結(jié)果無(wú)關(guān)緊要的)誤差存在。10.0001與10.001在鐵路工程師看來(lái)都是合格的。
雖然允許誤差存在,但程序員在使用浮點(diǎn)數(shù)進(jìn)行計(jì)算或邏輯處理時(shí),不注意,就可能出問(wèn)題。記住,永遠(yuǎn)不要直接比較兩個(gè)浮點(diǎn)的大小:
var a = 0.1 var b = 0.2 if (a + b === 0.3) { // doSomething }JS中如何進(jìn)入浮點(diǎn)數(shù)運(yùn)算
整數(shù)是完全精度的,不存在舍入誤差。例如,一些關(guān)于人民幣的運(yùn)算,都會(huì)以分為基本單位,計(jì)算采用分,展示再轉(zhuǎn)換成元。當(dāng)然,這樣也有一些問(wèn)題,會(huì)帶來(lái)額外的工作量,如果那天人民幣新增了一個(gè)貨幣單位,對(duì)系統(tǒng)的擴(kuò)展性也會(huì)有考驗(yàn)。
使用bignumber進(jìn)行運(yùn)算bignumber.js會(huì)在一定精度內(nèi),讓浮點(diǎn)數(shù)計(jì)算結(jié)果符合我們的期望。
{ let x = new BigNumber(0.1); let y = new BigNumber(0.2) let z = new BigNumber(0.3) console.log(z.equals(x.add(y))) // 0.3 === 0.1 + 0.2, true console.log(z.minus(x).equals(y)) // true console.log(z.minus(y).equals(x)) // true }
{ let x = 0.2 console.log(x * x === 0.04) // false let y = new BigNumber(0.2) let r = y.mul(y) // 0.04 console.log(r.equals(new BigNumber(0.04))) // true }
更多例子,可以看bignumber.js官方示例。
小結(jié)本文主要介紹了浮點(diǎn)數(shù)計(jì)算問(wèn)題,簡(jiǎn)單回答了為什么以及怎么辦兩個(gè)問(wèn)題:
為什么0.1 + 0.2 不等于0.3。因?yàn)橛?jì)算機(jī)不能精確表示0.1, 0.2這樣的浮點(diǎn)數(shù),計(jì)算時(shí)使用的是帶有舍入誤差的數(shù)
并不是所有的浮點(diǎn)數(shù)在計(jì)算機(jī)內(nèi)部都存在舍入誤差,比如0.5就沒(méi)有舍入誤差
具有舍入誤差的運(yùn)算結(jié)可能會(huì)符合我們的期望,原因可能是“負(fù)負(fù)得正”
怎么辦?1個(gè)辦法是使用整型代替浮點(diǎn)數(shù)計(jì)算;2是不要直接比較兩個(gè)浮點(diǎn)數(shù),而應(yīng)該使用bignumber.js這樣的浮點(diǎn)數(shù)運(yùn)算庫(kù)
最后,本文只是簡(jiǎn)單回答了為什么,如果讀者對(duì)更根本深入的原理感興趣,可以自行g(shù)oogle之。限于水平有限,本文如果有錯(cuò)誤,歡迎指正。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/90020.html
摘要:本文通過(guò)介紹的二進(jìn)制存儲(chǔ)標(biāo)準(zhǔn)來(lái)理解浮點(diǎn)數(shù)運(yùn)算精度問(wèn)題,和理解對(duì)象的等屬性值是如何取值的,最后介紹了一些常用的浮點(diǎn)數(shù)精度運(yùn)算解決方案。浮點(diǎn)數(shù)精度運(yùn)算解決方案關(guān)于浮點(diǎn)數(shù)運(yùn)算精度丟失的問(wèn)題,不同場(chǎng)景可以有不同的解決方案。 本文由云+社區(qū)發(fā)表 相信大家在平常的 JavaScript 開(kāi)發(fā)中,都有遇到過(guò)浮點(diǎn)數(shù)運(yùn)算精度誤差的問(wèn)題,比如 console.log(0.1+0.2===0.3)// fa...
摘要:返回是,這是為什么呢我們知道浮點(diǎn)數(shù)計(jì)算是不精確的,上面的返回式實(shí)際上是這樣的在的新規(guī)范加入了一個(gè)新的東西是在對(duì)象上面,新增一個(gè)極小的常量。根據(jù)規(guī)格,它表示與大于的最小浮點(diǎn)數(shù)之間的差。上面的代碼為浮點(diǎn)數(shù)運(yùn)算,部署了一個(gè)誤差檢查函數(shù)。 0.1+0.2 === 0.3 //返回是false, 這是為什么呢?? 我們知道浮點(diǎn)數(shù)計(jì)算是不精確的,上面的返回式實(shí)際上是這樣的:0.1 + 0.2 = ...
摘要:按照的數(shù)字格式,整數(shù)有的范圍是,而且只能表示有限個(gè)浮點(diǎn)數(shù),能表示的個(gè)數(shù)為個(gè)。 0.1+0.2 等于0.3嗎?相信拿著這條題目隨便問(wèn)一個(gè)高年級(jí)的小學(xué)生,他們都會(huì)毫不猶豫都回答:相等。是的,相等是正常的,這是常識(shí)。但是都說(shuō)實(shí)踐是檢驗(yàn)真理的唯一標(biāo)準(zhǔn),拿這道簡(jiǎn)單的算術(shù)題用javascript在chrome控制臺(tái)試驗(yàn)一下: 結(jié)果令人大跌眼鏡,在控制臺(tái)輸入0.1+0.2 == 0.3返回的結(jié)果竟然...
摘要:因此利用以及語(yǔ)法樹(shù)在代碼構(gòu)建過(guò)程中重寫(xiě)等符號(hào),開(kāi)發(fā)時(shí)直接以這樣的形式編寫(xiě)代碼,在構(gòu)建過(guò)程中編譯成,從而在開(kāi)發(fā)人員無(wú)感知的情況下解決計(jì)算失精的問(wèn)題,提升代碼的可讀性。 前言 你了解過(guò)0.1+0.2到底等于多少嗎?那0.1+0.7,0.8-0.2呢? 類(lèi)似于這種問(wèn)題現(xiàn)在已經(jīng)有了很多的解決方案,無(wú)論引入外部庫(kù)或者是自己定義計(jì)算函數(shù)最終的目的都是利用函數(shù)去代替計(jì)算。例如一個(gè)漲跌幅百分比的一個(gè)...
閱讀 3187·2021-11-24 09:39
閱讀 1057·2021-09-07 10:20
閱讀 2515·2021-08-23 09:45
閱讀 2393·2021-08-05 10:00
閱讀 655·2019-08-29 16:36
閱讀 903·2019-08-29 11:12
閱讀 2888·2019-08-26 11:34
閱讀 1888·2019-08-26 10:56