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