日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
JS數(shù)值存儲(chǔ)運(yùn)算原理

前言

相信大家都看過(guò)這些曾經(jīng)在社區(qū)比較火的文章:

專注于為中小企業(yè)提供成都網(wǎng)站設(shè)計(jì)、網(wǎng)站建設(shè)、外貿(mào)網(wǎng)站建設(shè)服務(wù),電腦端+手機(jī)端+微信端的三站合一,更高效的管理,為中小企業(yè)武侯免費(fèi)做網(wǎng)站提供優(yōu)質(zhì)的服務(wù)。我們立足成都,凝聚了一批互聯(lián)網(wǎng)行業(yè)人才,有力地推動(dòng)了上千多家企業(yè)的穩(wěn)健成長(zhǎng),幫助中小企業(yè)通過(guò)網(wǎng)站建設(shè)實(shí)現(xiàn)規(guī)模擴(kuò)充和轉(zhuǎn)變。

  • 0.1 + 0.2 與0.3為什么不相等?
  • 為什么 3.0000000000000002 === 3表達(dá)式為true ?
  • 等...

造成這些問(wèn)題的背后原因都是由于javaScript采用了 IEEE754 標(biāo)準(zhǔn),全稱 IEEE 二進(jìn)制浮點(diǎn)數(shù)算術(shù)標(biāo)準(zhǔn)。所以說(shuō)這個(gè)問(wèn)題其實(shí)不止是會(huì)在javaScript中出現(xiàn),而是「其他遵循 [IEEE 754]標(biāo)準(zhǔn)的語(yǔ)言也會(huì)出現(xiàn)這個(gè)問(wèn)題」

并且自己在最近的工作中也遇到了這個(gè)問(wèn)題,由于javaScript精度丟失而造成詭異問(wèn)題!

javaScript車禍現(xiàn)場(chǎng)

上面三個(gè)例子在我們?cè)诳刂婆_(tái)里面驗(yàn)證一遍,是不是瞬間覺(jué)得奇怪的知識(shí)又增加了?

javaScript這令人窒息的操作是不是讓很多后端人員口吐芬芳了,甚至是很多前端人員都覺(jué)得明明都是送分題,卻成了JS的送命題,工作中許多不經(jīng)意間寫出的bug,往往是由于JS的不按常理出牌。

說(shuō)了這么多,我們也改變不了這一現(xiàn)狀,那就嘗試去理解它吧~

計(jì)算機(jī)運(yùn)算

學(xué)過(guò)計(jì)算機(jī)相關(guān)同學(xué)都知道,我們的計(jì)算機(jī)底層元算采用的是二進(jìn)制,而不是我們平常用的十進(jìn)制!

二進(jìn)制

「為什么計(jì)算機(jī)要采用二進(jìn)制,而不是十進(jìn)制?」

以下是在知乎上看到的回答,我覺(jué)得這個(gè)理解是比較到位的。

計(jì)算機(jī)本身的理論模型,和采用哪個(gè)數(shù)學(xué)上的進(jìn)制完全無(wú)關(guān),十進(jìn)制也好,五進(jìn)制也好,二進(jìn)制也好,進(jìn)制在數(shù)學(xué)上都是等價(jià)的,并沒(méi)有哪個(gè)進(jìn)制擁有其他進(jìn)制無(wú)法實(shí)現(xiàn)的計(jì)算。

但計(jì)算機(jī)的實(shí)現(xiàn)是個(gè)工程問(wèn)題,需要和真實(shí)的物理環(huán)境打交道,我們現(xiàn)在是用電路去實(shí)現(xiàn)我們的計(jì)算機(jī)模型,那就需要和物理電路打交道,需要考慮到信號(hào)的衰減延遲,電路器件的各種電氣特性,什么電磁波干擾電流擾動(dòng),也就是會(huì)有失真的情況出現(xiàn),而要最大程度避免衰減,失真對(duì)計(jì)算機(jī)這個(gè)完美世界造成破壞,同時(shí)要考慮電路的設(shè)計(jì),制作成本,就需要最簡(jiǎn)單化的物理實(shí)現(xiàn)方案。

電子計(jì)算機(jī)確實(shí)是可以做成十進(jìn)制的,就像題主說(shuō)的像燈泡亮度分成十種亮度那樣,但與此同時(shí)會(huì)出現(xiàn)很多的工程問(wèn)題,比如對(duì)電子器件的精度和穩(wěn)定性要求很高,電路設(shè)計(jì)的復(fù)雜性提升等等,到頭來(lái)還不如就用二進(jìn)制,在成本和質(zhì)量上最劃算。

事實(shí)上,不但十進(jìn)制不行,十六進(jìn)制、八進(jìn)制、四進(jìn)制也都比不上二進(jìn)制。理論上已經(jīng)證明效率最高的進(jìn)制是e,離e最近的其實(shí)是三進(jìn)制。但三進(jìn)制不方便表示,不過(guò)也有人研究,前蘇聯(lián)就做過(guò)三進(jìn)制計(jì)算機(jī),國(guó)內(nèi)也有,但并沒(méi)有走出實(shí)驗(yàn)室。效率是一方面,實(shí)現(xiàn)成本又是一方面,最終大家還是覺(jué)得二進(jìn)制實(shí)現(xiàn)起來(lái)最方便。

原碼、反碼、補(bǔ)碼

「為運(yùn)算方便,機(jī)器數(shù)有 3 種表示法,即原碼、反碼和補(bǔ)碼」。

原碼

原碼是一種計(jì)算機(jī)中對(duì)數(shù)字的二進(jìn)制定點(diǎn)表示法。「原碼表示法在數(shù)值前面增加了一位符號(hào)位」。

反碼

正數(shù)的反碼和原碼一樣,

負(fù)數(shù)的反碼就是在原碼的基礎(chǔ)上符號(hào)位保持不變,其他位取反。

補(bǔ)碼

正數(shù)和 0 的補(bǔ)碼就是該數(shù)字本身。

「負(fù)數(shù)的補(bǔ)碼則是將其對(duì)應(yīng)正數(shù)按位取反再加 1」

二進(jìn)制轉(zhuǎn)換

「正整數(shù)的轉(zhuǎn)換方法」:除二取余,然后倒序排列,高位補(bǔ)零。

例如21的轉(zhuǎn)換

商  余
21/2  10  1
10/2  5   0
5/2   2   1
2/2   1   0
1/2   0   1

21的二進(jìn)制為10101,然后高位補(bǔ)0為00010101

「負(fù)整數(shù)的轉(zhuǎn)換方法」:將對(duì)應(yīng)的正整數(shù)轉(zhuǎn)換成二進(jìn)制后,對(duì)二進(jìn)制取反,然后對(duì)結(jié)果再加一。

例如-21
先把21轉(zhuǎn)換成二進(jìn)制 00010101
逐位取反:11101010
再加1:11101011(補(bǔ)碼)

「小數(shù)的轉(zhuǎn)換方法」:對(duì)小數(shù)點(diǎn)以后的數(shù)乘以2,取整數(shù)部分,再取小數(shù)部分乘2,以此類推……直到小數(shù)部分為0或位數(shù)足夠。取整部分按先后順序排列即可。

例如123.4:
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.4*2=0.8 ——————-> 取0
………… 后面就是循環(huán)了
按順序?qū)懗觯?.4 = 0.01100110……(0110循環(huán))
整數(shù)部分123的二進(jìn)制是 1111011
則123.4的二進(jìn)制表示為:1111011.011001100110……

發(fā)現(xiàn)了什么?十進(jìn)制小數(shù)轉(zhuǎn)二進(jìn)制后大概率出現(xiàn)無(wú)限位數(shù)!但我們的javaScript采用了「IEEE754」 標(biāo)準(zhǔn),全稱 「IEEE 二進(jìn)制浮點(diǎn)數(shù)算術(shù)標(biāo)準(zhǔn)」。

由于IEEE 754尾數(shù)位數(shù)限制,會(huì)將后面多余的位截掉。

javaScript 與 IEEE 754

“JavaScript 采用 IEEE 754 標(biāo)準(zhǔn),數(shù)值存儲(chǔ)為64位雙精度格式,數(shù)值精度最多可以達(dá)到 53 個(gè)二進(jìn)制位(1 個(gè)隱藏位與 52 個(gè)有效位)

在這個(gè)標(biāo)準(zhǔn)下,我們會(huì)用1位存儲(chǔ) S(sign),0 表示正數(shù),1 表示負(fù)數(shù)。用11位存儲(chǔ) E(exponent) + bias,對(duì)于11位來(lái)說(shuō),bias 的值是 2^(11-1) - 1,也就是 1023。用52 位存儲(chǔ) Fraction。

由于javaScript采用的是IEE754標(biāo)準(zhǔn),所以在進(jìn)制之間的轉(zhuǎn)換過(guò)程中可能會(huì)導(dǎo)致精度丟失,這是造成javaScript運(yùn)算翻車的罪魁禍?zhǔn)祝?/p>

破案

0.1+0.2 與 0.3為什么不相等?

0.1.toString(2)
// '0.0001100110011001100110011001100110011001100110011001101'   // 57
// 按 IEEE754 格式  57 - 4 = 52可以精確存儲(chǔ)
0.2.toString(2)
// '0.001100110011001100110011001100110011001100110011001101' // 56
// 按 IEEE754 格式  56 - 3 = 53  會(huì)丟棄最后一位數(shù)
0.3.toString(2)
// '0.010011001100110011001100110011001100110011001100110011'  // 56
// 按 IEEE754 格式  56 - 2 = 54  會(huì)丟棄最后兩位數(shù)

/*總結(jié):
存儲(chǔ)0.1沒(méi)有誤差, 存儲(chǔ) 0.2丟棄最后一位 1  存儲(chǔ)0.3丟棄最后2位 11,
顯然存儲(chǔ)0.3丟棄的數(shù)值>存儲(chǔ)0.2丟棄的數(shù)值
經(jīng)分析 0.1 + 0.2   應(yīng)該大于 0.3
*/
0.1 + 0.2 > 0.3  // true

為什么 3.0000000000000002 === 3表達(dá)式為true ?

手動(dòng)將 3.0000_0000_0000_0002轉(zhuǎn)換成二進(jìn)制浮點(diǎn)數(shù)

整數(shù)部分為 11?

小數(shù)部分0.0000_0000_0000_0002

0.0000_0000_0000_0002.toString(2)

'0.0000000000000000000000000000000000000000000000000000111001101001010110010100101111101100010001001101111'

注意小數(shù)點(diǎn)后面正好有52個(gè)0

0.0000_0000_0000_0002.toString(2).length  // 105 105

將 3.0000000000000002 用 IEEE754 格式表示

  1. 符號(hào)S: 正數(shù),0
  2. 指數(shù)位E:11 = 1.1 * 2^1 (二進(jìn)制),E = 1023 + 1 = 1024 = 10000000000(二進(jìn)制)
  3. 尾數(shù)位M:0.1.....0

所以該浮點(diǎn)數(shù)格式為: 0   1000_0000_000   1...000(一共52個(gè)0) 這個(gè)數(shù)正好是3。

如何解決精度問(wèn)題?

目前有許多第三方庫(kù)可以解決javaScript精度丟失的問(wèn)題

math.js

math.js是JavaScript和Node.js的一個(gè)廣泛的數(shù)學(xué)庫(kù)。支持?jǐn)?shù)字,大數(shù),復(fù)數(shù),分?jǐn)?shù),單位和矩陣等數(shù)據(jù)類型的運(yùn)算。

官網(wǎng):mathjs.org/ GitHub:github.com/josdejong/m…

0.1+0.2 ===0.3實(shí)現(xiàn)代碼:

var math = require('mathjs')
console.log(math.add(0.1,0.2))//0.30000000000000004
console.log(math.format((math.add(math.bignumber(0.1),math.bignumber(0.2)))))//'0.3'

decimal.js

為 JavaScript 提供十進(jìn)制類型的任意精度數(shù)值。

官網(wǎng):mikemcl.github.io/decimal.js/

GitHub:github.com/MikeMcl/dec…

var Decimal = require('decimal.js')
x = new  Decimal(0.1)
y = 0.2
console.log(x.plus(y).toString())//'0.3'

bignumber.js

用于任意精度算術(shù)的JavaScript庫(kù)。

官網(wǎng):mikemcl.github.io/bignumber.j…

Github:github.com/MikeMcl/big…

var BigNumber = require("bignumber.js")
x = new BigNumber(0.1)
y = 0.2
console.log(x.plus(y).toString()) //'0.3'

標(biāo)題名稱:JS數(shù)值存儲(chǔ)運(yùn)算原理
文章路徑:http://www.dlmjj.cn/article/djsohse.html