亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

「干貨」細(xì)說 Javascript 中的浮點(diǎn)數(shù)精度丟失問題(內(nèi)附好課推薦)

senntyou / 2474人閱讀

摘要:前言最近,朋友問了我這樣一個(gè)問題在中的運(yùn)算結(jié)果,為什么是這樣的雖然我告訴他說,這是由于浮點(diǎn)數(shù)精度問題導(dǎo)致的。由于可以用階碼移動(dòng)小數(shù)點(diǎn),因此稱為浮點(diǎn)數(shù)。它的實(shí)現(xiàn)遵循標(biāo)準(zhǔn),使用位精度來表示浮點(diǎn)數(shù)。

前言

最近,朋友 L 問了我這樣一個(gè)問題:在 chrome 中的運(yùn)算結(jié)果,為什么是這樣的?

0.55 * 100 // 55.00000000000001
0.56 * 100 // 56.00000000000001
0.57 * 100 // 56.99999999999999
0.58 * 100 // 57.99999999999999
0.59 * 100 // 59
0.60 * 100 // 60

雖然我告訴他說,這是由于浮點(diǎn)數(shù)精度問題導(dǎo)致的。但他還是不太明白,為何有的結(jié)果輸出整數(shù),有的是以 ...001 的小數(shù)結(jié)尾,有的卻是以 ...999 的小數(shù)結(jié)尾,跟預(yù)想中的有差異。

這其實(shí)牽涉到了計(jì)算機(jī)原理的知識,真要解釋清楚什么是浮點(diǎn)數(shù),恐怕得分好幾個(gè)章節(jié)了。想深入了解的同學(xué),可以前往 這篇文章 細(xì)讀。今天我們僅討論浮點(diǎn)數(shù)運(yùn)算結(jié)果的成因,以及如何實(shí)現(xiàn)我們期望的結(jié)果。

浮點(diǎn)數(shù)與 IEEE 754

在解釋什么是浮點(diǎn)數(shù)之前,讓我們先從較為簡單的小數(shù)點(diǎn)說起。

小數(shù)點(diǎn),在數(shù)制中代表一種對齊方式。比如要比較 1000 和 200 哪個(gè)比較大,該怎么做呢?必須把他們右對齊:

1000
 200

發(fā)現(xiàn) 1 比 0(前面補(bǔ)零)大,所以 1000 比較大。那么如果要比較 1000 和 200.01 呢?這時(shí)候就不是右對齊了,而應(yīng)該是以小數(shù)點(diǎn)對齊:

1000
 200.01

小數(shù)點(diǎn)的位置,在進(jìn)制表示中是至關(guān)重要的。位置差一位整體就要差進(jìn)制倍(十進(jìn)制就是十倍)。在計(jì)算機(jī)中也是這樣,雖然計(jì)算機(jī)使用二進(jìn)制,但在處理非整數(shù)時(shí),也需要考慮小數(shù)點(diǎn)的位置問題。無法對齊小數(shù)點(diǎn),就無法做加減法比較這樣的操作。

接下來的一個(gè)重要概念:在計(jì)算機(jī)中的小數(shù)有兩種,定點(diǎn) 和 浮點(diǎn)。

定點(diǎn)的意思是,小數(shù)點(diǎn)固定在 32 位中的某個(gè)位置,前面的是整數(shù),后面的是小數(shù)。小數(shù)點(diǎn)具體固定在哪里,可以自己在程序中指定。定點(diǎn)數(shù)的優(yōu)點(diǎn)是很簡單,大部分運(yùn)算實(shí)現(xiàn)起來和整數(shù)一樣或者略有變化,但是缺點(diǎn)則是表示范圍太小,精度很差,不能充分運(yùn)用存儲單元。

浮點(diǎn)數(shù)就是設(shè)計(jì)來克服這個(gè)缺點(diǎn)的,它相當(dāng)于一個(gè)定點(diǎn)數(shù)加上一個(gè)階碼,階碼表示將這個(gè)定點(diǎn)數(shù)的小數(shù)點(diǎn)移動(dòng)若干位。由于可以用階碼移動(dòng)小數(shù)點(diǎn),因此稱為浮點(diǎn)數(shù)。我們在寫程序時(shí),用到小數(shù)的地方,用 float 類型表示,可以方便快速地對小數(shù)進(jìn)行運(yùn)算。

浮點(diǎn)數(shù)在 Javascript 中的存儲,與其他語言如 Java 和 Python 不同。所有數(shù)字(包括整數(shù)和小數(shù))都只有一種類型 — Number。它的實(shí)現(xiàn)遵循 IEEE 754 標(biāo)準(zhǔn),使用64位精度來表示浮點(diǎn)數(shù)。它是目前最廣泛使用的格式,該格式用 64 位二進(jìn)制表示像下面這樣:

從上圖中可以看出,這 64 位分為三個(gè)部分:

符號位:1 位用于標(biāo)志位。用來表示一個(gè)數(shù)是正數(shù)還是負(fù)數(shù)

指數(shù)位:11 位用于指數(shù)。這允許指數(shù)最大到 1024

尾數(shù)位:剩下的 52 位代表的是尾數(shù),超出的部分自動(dòng)進(jìn)一舍零

精度丟哪兒去了?

問:要把小數(shù)裝入計(jì)算機(jī),總共分幾步?

答:3 步。
第一步:轉(zhuǎn)換成二進(jìn)制
第二步:用二進(jìn)制科學(xué)計(jì)算法表示
第三步:表示成 IEEE 754 形式
但第一步和第三步都有可能 丟失精度。

十進(jìn)制是給人看的。但在進(jìn)行運(yùn)算之前,必須先轉(zhuǎn)換為計(jì)算機(jī)能處理的二進(jìn)制。最后,當(dāng)運(yùn)算完畢后,再將結(jié)果轉(zhuǎn)換回十進(jìn)制,繼續(xù)給人看。精度就丟失于這兩次轉(zhuǎn)換的過程中。

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

接下來,就具體說說轉(zhuǎn)換的過程。來看一個(gè)簡單的例子:

如何將十進(jìn)制的 168.45 轉(zhuǎn)換為二進(jìn)制?

讓我們拆為兩個(gè)部分來解析:

1、整數(shù)部分。它的轉(zhuǎn)換方法是,除 2 取余法。即每次將整數(shù)部分除以 2,余數(shù)為該位權(quán)上的數(shù),而商繼續(xù)除以 2,余數(shù)又為上一個(gè)位權(quán)上的數(shù),這個(gè)步驟一直持續(xù)下去,直到商為 0 為止,最后讀數(shù)時(shí)候,從最后一個(gè)余數(shù)讀起,一直到最前面的一個(gè)余數(shù)。

所以整數(shù)部分 168 的轉(zhuǎn)換過程如下:

第一步,將 168 除以 2,商 84,余數(shù)為 0。

第二步,將商 84 除以 2,商 42 余數(shù)為 0。

第三步,將商 42 除以 2,商 21 余數(shù)為 0。

第四步,將商 21 除以 2,商 10 余數(shù)為 1。

第五步,將商 10 除以 2,商 5 余數(shù)為 0。

第六步,將商 5 除以 2,商 2 余數(shù)為 1。

第七步,將商 2 除以 2,商 1 余數(shù)為 0。

第八步,將商 1 除以 2,商 0 余數(shù)為 1。

第九步,讀數(shù)。因?yàn)樽詈笠晃皇墙?jīng)過多次除以 2 才得到的,因此它是最高位。讀數(shù)的時(shí)候,從最后的余數(shù)向前讀,即 10101000。

2、小數(shù)部分。它的轉(zhuǎn)換方法是,乘 2 取整法。即將小數(shù)部分乘以 2,然后取整數(shù)部分,剩下的小數(shù)部分繼續(xù)乘以 2,然后再取整數(shù)部分,剩下的小數(shù)部分又乘以 2,一直取到小數(shù)部分為 0 為止。如果永遠(yuǎn)不能為零,就同十進(jìn)制數(shù)的四舍五入一樣,按照要求保留多少位小數(shù)時(shí),就根據(jù)后面一位是 0 還是 1 進(jìn)行取舍。如果是 0 就舍掉,如果是 1 則入一位,換句話說就是,0 舍 1 入。讀數(shù)的時(shí)候,要從前面的整數(shù)開始,讀到后面的整數(shù)。

所以小數(shù)部分 0.45 (保留到小數(shù)點(diǎn)第四位)的轉(zhuǎn)換過程如下:

第一步,將 0.45 乘以 2,得 0.9,則整數(shù)部分為 0,小數(shù)部分為 0.9。

第二步, 將小數(shù)部分 0.9 乘以 2,得 1.8,則整數(shù)部分為 1,小數(shù)部分為 0.8。

第三步, 將小數(shù)部分 0.8 乘以 2,得 1.6,則整數(shù)部分為 1,小數(shù)部分為 0.6。

第四步,將小數(shù)部分 0.6 乘以 2,得 1.2,則整數(shù)部分為 1,小數(shù)部分為 0.2。

第五步,將小數(shù)部分 0.2 乘以 2,得 0.4,則整數(shù)部分為 0,小數(shù)部分為 0.4。

第六步,將小數(shù)部分 0.4 乘以 2,得 0.8,則整數(shù)部分為 0,小數(shù)部分為 0.8。

...

可以看到,從第六步開始,將無限循環(huán)第三、四、五步,一直乘下去,最后不可能得到小數(shù)部分為 0。因此,這個(gè)時(shí)候只好學(xué)習(xí)十進(jìn)制的方法進(jìn)行四舍五入了。但是二進(jìn)制只有 0 和 1 兩個(gè),于是就出現(xiàn) 0 舍 1 入的 “口訣” 了,這也是計(jì)算機(jī)在轉(zhuǎn)換中會產(chǎn)生誤差的根本原因。但是由于保留位數(shù)很多,精度很高,所以可以忽略不計(jì)。

這樣,我們就可以得出十進(jìn)制數(shù) 168.45 轉(zhuǎn)換為二進(jìn)制的結(jié)果,約等于 10101000.0111。

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

它的轉(zhuǎn)換方法相對簡單些,按權(quán)相加法。就是將二進(jìn)制每位上的數(shù)乘以權(quán),然后相加之和即是十進(jìn)制數(shù)。其中有兩個(gè)注意點(diǎn):要知道二進(jìn)制每位的權(quán)值,要能求出每位的值。

所以,將剛才的二進(jìn)制 10101000.0111 轉(zhuǎn)換為十進(jìn)制,得到的結(jié)果就是 168.4375,再四舍五入一下,即 168.45。

解決方案

正如本文開頭所提到的,在 JavaScript 中進(jìn)行浮點(diǎn)數(shù)的運(yùn)算,會有不少奇葩的問題。在明白了產(chǎn)生問題的根本原因之后,當(dāng)然是想辦法解決啦~

一個(gè)簡單粗暴的建議是,使用像 mathjs 這樣的庫。它的 API 也挺簡單的:

// load math.js
const math = require("mathjs")

// functions and constants
math.round(math.e, 3)             // 2.718
math.atan2(3, -3) / math.pi       // 0.75

// expressions
math.eval("12 / (2.3 + 0.7)")     // 4
math.eval("12.7 cm to inch")      // 5 inch
math.eval("sin(45 deg) ^ 2")      // 0.5

// chaining
math.chain(3)
    .add(4)
    .multiply(2)
    .done()  // 14

但如果在工程中,沒有太多需要進(jìn)行運(yùn)算的場景的話,就不建議這么做了。畢竟引入三方庫也是有成本的,無論是學(xué)習(xí) API,還是引入庫之后,帶來打包后的文件體積增積。

那么,不引入庫該怎么處理浮點(diǎn)數(shù)呢?

可以從需求出發(fā)。例如,本文開頭的例子??梢圆孪氲?,需求可能是要把小數(shù)轉(zhuǎn)為百分比,通常會保留兩位小數(shù)。而在一些對數(shù)字較為敏感的業(yè)務(wù)場景中,可能并不希望對數(shù)字進(jìn)行四舍五入,所以 toFixed() 方法就沒法用了。

一種思路是,將小數(shù)點(diǎn)像右多移動(dòng) n 位,取整后再除以 (10 * n)。比如這樣:

0.58 * 10000 / 100 // => 58

ok,搞定~

特別需要注意的是,在需要四舍五入的場景下,我們會習(xí)慣用到內(nèi)置方法 toFixed(),但它存在一些問題:

1.35.toFixed(1) // 1.4 正確
1.335.toFixed(2) // 1.33  錯(cuò)誤
1.3335.toFixed(3) // 1.333 錯(cuò)誤
1.33335.toFixed(4) // 1.3334 正確
1.333335.toFixed(5)  // 1.33333 錯(cuò)誤
1.3333335.toFixed(6) // 1.333333 錯(cuò)誤

另外,它的返回結(jié)果類型是 String。不能直接拿來做運(yùn)算,因?yàn)橛?jì)算機(jī)會認(rèn)為是 字符串拼接。

總結(jié)

計(jì)算機(jī)在做運(yùn)算的時(shí)候,會分三個(gè)步驟。其中,將十進(jìn)制轉(zhuǎn)為二進(jìn)制,再將二進(jìn)制轉(zhuǎn)為十進(jìn)制的時(shí)候,都會產(chǎn)生精度丟失。

使用庫,是最簡單粗暴的解決方案。但如果使用不頻繁,還是要根據(jù)需求,手動(dòng)解決。在使用內(nèi)置方法 toFixed() 的時(shí)候,要特別注意它的返回類型,不要直接拿來做運(yùn)算。

好課推薦

近期公眾號后臺有多位讀者留言,金三銀四求職卻頻頻遇阻,詢問有沒有什么體系性、針對性的內(nèi)容可以看看。

最近我正好在 gitChat 上看到了,來自百度的大佬 LucasHC侯策) 的系列課程《前端開發(fā) 核心知識進(jìn)階》,因?yàn)榘葑x過大佬寫的書 《React 狀態(tài)管理與同構(gòu)實(shí)戰(zhàn)》,所以就買了這門課程。

這門課程 共 50 講,從 36 個(gè)熱門主題 切入講解高頻面試題,以及會深度剖析底層原理,干貨滿滿,甚至還有不少大佬自己作為 “BAT” 面試官多年的 “私房題”,以及面試時(shí)遇到的 “經(jīng)典題”,非常實(shí)用了。

而且剛好現(xiàn)在在搞 特價(jià) 69 元,特價(jià)到5月7號結(jié)束,沒幾天了。掃描下圖二維碼就可以學(xué)習(xí),需要的拿走不謝。

PS:歡迎關(guān)注我的公眾號 “超哥前端小棧”,交流更多的想法與技術(shù)。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/103820.html

相關(guān)文章

  • 深度剖析0.1 +0.2===0.30000000000000004的原因

    摘要:吐槽一句,大二的專業(yè)課數(shù)字邏輯電路終于用在工作上了。,整數(shù)位為,且精度只到十分位,因此是。如果是不限精度的話,轉(zhuǎn)換后的二進(jìn)制數(shù)應(yīng)該是無限循環(huán)。再看一下百科給出的標(biāo)準(zhǔn)因此,的類型,最高的位是符號位,接著的位是指數(shù),剩下的位為有效數(shù)字。 showImg(https://segmentfault.com/img/remote/1460000011902479?w=600&h=600); 用一...

    haobowd 評論0 收藏0
  • 為什么0.1+0.2不等于0.3

    摘要:又如,對于,結(jié)果其實(shí)并不是,但是最接近真實(shí)結(jié)果的數(shù),比其它任何浮點(diǎn)數(shù)都更接近。許多語言也就直接顯示結(jié)果為了,而不展示一個(gè)浮點(diǎn)數(shù)的真實(shí)結(jié)果了。小結(jié)本文主要介紹了浮點(diǎn)數(shù)計(jì)算問題,簡單回答了為什么以及怎么辦兩個(gè)問題為什么不等于。 原文地址:為什么0.1+0.2不等于0.3 先看兩個(gè)簡單但詭異的代碼: 0.1 + 0.2 > 0.3 // true 0.1 * 0.1 = 0.01000000...

    Profeel 評論0 收藏0
  • JS中如何理解浮點(diǎn)數(shù)?

    摘要:本文通過介紹的二進(jìn)制存儲標(biāo)準(zhǔn)來理解浮點(diǎn)數(shù)運(yùn)算精度問題,和理解對象的等屬性值是如何取值的,最后介紹了一些常用的浮點(diǎn)數(shù)精度運(yùn)算解決方案。浮點(diǎn)數(shù)精度運(yùn)算解決方案關(guān)于浮點(diǎn)數(shù)運(yùn)算精度丟失的問題,不同場景可以有不同的解決方案。 本文由云+社區(qū)發(fā)表 相信大家在平常的 JavaScript 開發(fā)中,都有遇到過浮點(diǎn)數(shù)運(yùn)算精度誤差的問題,比如 console.log(0.1+0.2===0.3)// fa...

    bang590 評論0 收藏0
  • JS魔法堂:徹底理解0.1 + 0.2 === 0.30000000000000004的背后

    摘要:也就是說不僅是會產(chǎn)生這種問題,只要是采用的浮點(diǎn)數(shù)編碼方式來表示浮點(diǎn)數(shù)時(shí),則會產(chǎn)生這類問題。到這里我們都理解只要采取的浮點(diǎn)數(shù)編碼的語言均會出現(xiàn)上述問題,只是它們的標(biāo)準(zhǔn)類庫已經(jīng)為我們提供了解決方案而已。 Brief 一天有個(gè)朋友問我JS中計(jì)算0.7 * 180怎么會等于125.99999999998,坑也太多了吧!那時(shí)我猜測是二進(jìn)制表示數(shù)值時(shí)發(fā)生round-off error所導(dǎo)致,但并不...

    JerryWangSAP 評論0 收藏0
  • 從一個(gè) bug 看 javascript精度丟失問題

    摘要:就像一些無理數(shù)不能有限表示,如圓周率,等。遵循規(guī)范,采用雙精度存儲,占用。參考中不會失去精度的最大值數(shù)字精度丟失的一些典型問題 問題描述 后端返回 { spaceObject: { objectId: 1049564069045993472 } } 前端模版,使用的是 atpl 模版 前端獲取 objectId 的方式,const objectId = $(#test).da...

    NusterCache 評論0 收藏0

發(fā)表評論

0條評論

最新活動(dòng)
閱讀需要支付1元查看
<