摘要:推導(dǎo)為何等于在中所有數(shù)值都以標(biāo)準(zhǔn)的雙精度浮點(diǎn)數(shù)進(jìn)行存儲(chǔ)的。先來(lái)了解下標(biāo)準(zhǔn)下的雙精度浮點(diǎn)數(shù)。精度位總共是,因?yàn)橛每茖W(xué)計(jì)數(shù)法表示,所以首位固定的就沒(méi)有占用空間。驗(yàn)證完成的最大安全數(shù)是如何來(lái)的根據(jù)雙精度浮點(diǎn)數(shù)的構(gòu)成,精度位數(shù)是。
閱讀完本文可以了解到 0.1 + 0.2 為什么等于 0.30000000000000004 以及 JavaScript 中最大安全數(shù)是如何來(lái)的。
十進(jìn)制小數(shù)轉(zhuǎn)為二進(jìn)制小數(shù)方法拿 173.8125 舉例如何將之轉(zhuǎn)化為二進(jìn)制小數(shù)。
①. 針對(duì)整數(shù)部分 173,采取除 2 取余,逆序排列;
173 / 2 = 86 ... 1 86 / 2 = 43 ... 0 43 / 2 = 21 ... 1 ↑ 21 / 2 = 10 ... 1 | 逆序排列 10 / 2 = 5 ... 0 | 5 / 2 = 2 ... 1 | 2 / 2 = 1 ... 0 1 / 2 = 0 ... 1
得整數(shù)部分的二進(jìn)制為 10101101。
②. 針對(duì)小數(shù)部分 0.8125,采用乘 2 取整,順序排列;
0.8125 * 2 = 1.625 | 0.625 * 2 = 1.25 | 順序排列 0.25 * 2 = 0.5 | 0.5 * 2 = 1 ↓
得小數(shù)部分的二進(jìn)制為 1101。
③. 將前面兩部的結(jié)果相加,結(jié)果為 10101101.1101;
小心,二進(jìn)制小數(shù)丟失了精度!根據(jù)上面的知識(shí),將十進(jìn)制小數(shù) 0.1 轉(zhuǎn)為二進(jìn)制:
0.1 * 2 = 0.2 0.2 * 2 = 0.4 // 注意這里 0.4 * 2 = 0.8 0.8 * 2 = 1.6 0.6 * 2 = 1.2 0.2 * 2 = 0.4 // 注意這里,循環(huán)開(kāi)始 0.4 * 2 = 0.8 0.8 * 2 = 1.6 0.6 * 2 = 1.2 ...
可以發(fā)現(xiàn)有限十進(jìn)制小數(shù) 0.1 卻轉(zhuǎn)化成了無(wú)限二進(jìn)制小數(shù) 0.00011001100...,可以看到精度在轉(zhuǎn)化過(guò)程中丟失了!
能被轉(zhuǎn)化為有限二進(jìn)制小數(shù)的十進(jìn)制小數(shù)的最后一位必然以 5 結(jié)尾(因?yàn)橹挥?0.5 * 2 才能變?yōu)檎麛?shù))。所以十進(jìn)制中一位小數(shù) 0.1 ~ 0.9 當(dāng)中除了 0.5 之外的值在轉(zhuǎn)化成二進(jìn)制的過(guò)程中都丟失了精度。
推導(dǎo) 0.1 + 0.2 為何等于 0.30000000000000004在 JavaScript 中所有數(shù)值都以 IEEE-754 標(biāo)準(zhǔn)的 64 bit 雙精度浮點(diǎn)數(shù)進(jìn)行存儲(chǔ)的。先來(lái)了解下 IEEE-754 標(biāo)準(zhǔn)下的雙精度浮點(diǎn)數(shù)。
這幅圖很關(guān)鍵,可以從圖中看到 IEEE-754 標(biāo)準(zhǔn)下雙精度浮點(diǎn)數(shù)由三部分組成,分別如下:
sign(符號(hào)): 占 1 bit, 表示正負(fù);
exponent(指數(shù)): 占 11 bit,表示范圍;
mantissa(尾數(shù)): 占 52 bit,表示精度,多出的末尾如果是 1 需要進(jìn)位;
推薦閱讀 JavaScript 浮點(diǎn)數(shù)陷阱及解法,閱讀完該文后可以了解到以下公式的由來(lái)。
精度位總共是 53 bit,因?yàn)橛每茖W(xué)計(jì)數(shù)法表示,所以首位固定的 1 就沒(méi)有占用空間。即公式中 (M + 1) 里的 1。另外公式里的 1023 是 2^11 的一半。小于 1023 的用來(lái)表示小數(shù),大于 1023 的用來(lái)表示整數(shù)。指數(shù)可以控制到 2^1024 - 1,而精度最大只達(dá)到 2^53 - 1,兩者相比可以得出 JavaScript 實(shí)際可以精確表示的數(shù)字其實(shí)很少。
0.1 轉(zhuǎn)化為二進(jìn)制為 0.0001100110011...,用科學(xué)計(jì)數(shù)法表示為 1.100110011... x 2^(-4),根據(jù)上述公式,S 為 0(1 bit),E 為 -4 + 1023,對(duì)應(yīng)的二進(jìn)制為 01111111011(11 bit),M 為 1001100110011001100110011001100110011001100110011010(52 bit,另外注意末尾的進(jìn)位),0.1 的存儲(chǔ)示意圖如下:
同理,0.2 轉(zhuǎn)化為二進(jìn)制為 0.001100110011...,用科學(xué)計(jì)數(shù)法表示為 1.100110011... x 2^(-3),根據(jù)上述公式,E 為 -3 + 1023,對(duì)應(yīng)的二進(jìn)制為 01111111100, M 為 1001100110011001100110011001100110011001100110011010, 0.2 的存儲(chǔ)示意圖如下:
0.1 + 0.2 即 2^(-4) x 1.1001100110011001100110011001100110011001100110011010 與 2^(-3) x 1.1001100110011001100110011001100110011001100110011010 之和
// 計(jì)算過(guò)程 0.00011001100110011001100110011001100110011001100110011010 0.0011001100110011001100110011001100110011001100110011010 // 相加得 0.01001100110011001100110011001100110011001100110011001110
0.01001100110011001100110011001100110011001100110011001110 轉(zhuǎn)化為十進(jìn)制就是 0.30000000000000004。驗(yàn)證完成!
JavaScript 的最大安全數(shù)是如何來(lái)的根據(jù)雙精度浮點(diǎn)數(shù)的構(gòu)成,精度位數(shù)是 53 bit。安全數(shù)的意思是在 -2^53 ~ 2^53 內(nèi)的整數(shù)(不包括邊界)與唯一的雙精度浮點(diǎn)數(shù)互相對(duì)應(yīng)。舉個(gè)例子比較好理解:
Math.pow(2, 53) === Math.pow(2, 53) + 1 // true
Math.pow(2, 53) 竟然與 Math.pow(2, 53) + 1 相等!這是因?yàn)?Math.pow(2, 53) + 1 已經(jīng)超過(guò)了尾數(shù)的精度限制(53 bit),在這個(gè)例子中 Math.pow(2, 53) 和 Math.pow(2, 53) + 1 對(duì)應(yīng)了同一個(gè)雙精度浮點(diǎn)數(shù)。所以 Math.pow(2, 53) 就不是安全數(shù)了。
最大的安全數(shù)為 Math.pow(2, 53) - 1,即 9007199254740991。業(yè)務(wù)中碰到的精度問(wèn)題以及解決方案
了解 JavaScript 精度問(wèn)題對(duì)我們業(yè)務(wù)有什么幫助呢?舉個(gè)業(yè)務(wù)場(chǎng)景:比如有個(gè)訂單號(hào)后端 Java 同學(xué)定義的是 long 類型,但是當(dāng)這個(gè)訂單號(hào)轉(zhuǎn)換成 JavaScript 的 Number 類型時(shí)候精度會(huì)丟失了,那沒(méi)有以上知識(shí)鋪墊那就理解不了精度為什么會(huì)丟失。
解決方案大致有以下幾種:
1.針對(duì)大數(shù)的整數(shù)可以考慮使用 bigint 類型(目前在 stage 3 階段);
2.使用 bigNumber,它的思想是轉(zhuǎn)化成 string 進(jìn)行處理,這種方式對(duì)性能有一定影響;
3.可以考慮使用 long.js,它的思想是將 long 類型的值轉(zhuǎn)化成兩個(gè) 32 位的雙精度類型的值。
4.針對(duì)小數(shù)可以考慮 JavaScript 浮點(diǎn)數(shù)陷阱及解法 里面提到的方案;
相關(guān)鏈接代碼之謎系列
IEEE-754 進(jìn)制轉(zhuǎn)換圖生成
JavaScript 浮點(diǎn)數(shù)陷阱及解法: 推薦閱讀
javascript 里最大的安全的整數(shù)為什么是2的53次方減一
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/98141.html
摘要:前言最近,朋友問(wèn)了我這樣一個(gè)問(wèn)題在中的運(yùn)算結(jié)果,為什么是這樣的雖然我告訴他說(shuō),這是由于浮點(diǎn)數(shù)精度問(wèn)題導(dǎo)致的。由于可以用階碼移動(dòng)小數(shù)點(diǎn),因此稱為浮點(diǎn)數(shù)。它的實(shí)現(xiàn)遵循標(biāo)準(zhǔn),使用位精度來(lái)表示浮點(diǎn)數(shù)。 showImg(https://segmentfault.com/img/remote/1460000018981071); 前言 最近,朋友 L 問(wèn)了我這樣一個(gè)問(wèn)題:在 chrome 中的運(yùn)算...
摘要:排除直接使用的數(shù)太大或太小超出范圍,出現(xiàn)這種問(wèn)題的情況基本是浮點(diǎn)數(shù)的小數(shù)部分在轉(zhuǎn)成二進(jìn)制時(shí)丟失了精度,所以我們可以將小數(shù)部分也轉(zhuǎn)換成整數(shù)后再計(jì)算。 // 1. 兩數(shù)相加 // 0.1 + 0.2 = 0.30000000000000004 // 0.7 + 0.1 = 0.7999999999999999 // 0.2 + 0.4 = 0.6000000000000001 // 2.2...
摘要:方法使用定點(diǎn)表示法來(lái)格式化一個(gè)數(shù),會(huì)對(duì)結(jié)果進(jìn)行四舍五入。該數(shù)值在必要時(shí)進(jìn)行四舍五入,另外在必要時(shí)會(huì)用來(lái)填充小數(shù)部分,以便小數(shù)部分有指定的位數(shù)。如果數(shù)值大于,該方法會(huì)簡(jiǎn)單調(diào)用并返回一個(gè)指數(shù)記數(shù)法格式的字符串。在環(huán)境中,只能是之間,測(cè)試版本為。 showImg(https://segmentfault.com/img/remote/1460000011913134?w=768&h=521)...
摘要:與此相對(duì),強(qiáng)類型語(yǔ)言的類型之間不一定有隱式轉(zhuǎn)換。三為什么是弱類型弱類型相對(duì)于強(qiáng)類型來(lái)說(shuō)類型檢查更不嚴(yán)格,比如說(shuō)允許變量類型的隱式轉(zhuǎn)換,允許強(qiáng)制類型轉(zhuǎn)換等等。在中,加性運(yùn)算符有大量的特殊行為。 從++[[]][+[]]+[+[]]==10?深入淺出弱類型JS的隱式轉(zhuǎn)換 本文純屬原創(chuàng)? 如有雷同? 純屬抄襲? 不甚榮幸! 歡迎轉(zhuǎn)載! 原文收錄在【我的GitHub博客】,覺(jué)得本文寫的不算爛的...
閱讀 1141·2021-11-16 11:45
閱讀 3152·2021-10-13 09:40
閱讀 747·2019-08-26 13:45
閱讀 1245·2019-08-26 13:32
閱讀 2198·2019-08-26 13:23
閱讀 943·2019-08-26 12:16
閱讀 2847·2019-08-26 11:37
閱讀 1781·2019-08-26 10:32