国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > JavaScript > 正文

淺談JavaScript中小數和大整數的精度丟失

2019-11-20 09:50:54
字體:
來源:轉載
供稿:網友

先來看兩個問題:

0.1 + 0.2 == 0.3; // false
9999999999999999 == 10000000000000000; // true

第一個問題是小數的精度問題,在業界不少博客里已有討論。第二個問題,去年公司有個系統的數據庫在做數據訂正時,發現有部分數據重復的詭異現象。本文將從規范出發,對上面的問題做個小結。

最大整數

JavaScript 中的數字是用 IEEE 754 雙精度 64 位浮點數 來存儲的,其格式為:

s x m x 2^e

s 是符號位,表示正負。 m 是尾數,有 52 bits. e 是指數,有 11 bits. 在 ECMAScript 規范 里有給出 e 的范圍為 [-1074, 971]. 這樣,很容易推導出 JavaScript 能表示的最大整數為:

1 x (2^53 - 1) x 2^971 = 1.7976931348623157e+308

這個值正是 Number.MAX_VALUE

同理可推導出 Number.MIN_VALUE 的值為:

1 x 1 x 2^(-1074) = 5e-324

注意 MIN_VALUE 表示最接近 0 的正數,而不是最小的數。最小的數是 -Number.MAX_VALUE

小數的精度丟失

JavaScript 的數字都是雙精度浮點數,在計算機里用二進制存儲。當有效位數超過 52 位時,會存在精度丟失。比如:

十進制 0.1 的二進制為 0.0 0011 0011 0011 … (循環 0011)
十進制 0.2 的二進制為 0.0011 0011 0011 … (循環 0011)
0.1 + 0.2 相加可表示為:
   e = -4; m = 1.10011001100...1100(52 位)
 + e = -3; m = 1.10011001100...1100(52 位)
---------------------------------------------
   e = -3; m = 0.11001100110...0110
 + e = -3; m = 1.10011001100...1100
---------------------------------------------
   e = -3; m = 10.01100110011...001
---------------------------------------------
 = 0.01001100110011...001
 = 0.30000000000000004(十進制)

根據上面的演算,還可以得出一個結論:當十進制小數的二進制表示的有限數字不超過 52 位時,在 JavaScript 里是可以精確存儲的。比如:

0.05 + 0.005 == 0.055 // true

進一步的規律,比如:

0.05 + 0.2 == 0.25 // true
0.05 + 0.9 == 0.95 // false

需要考慮 IEEE 754 的 Rounding modes, 有興趣的可進一步研究。

大整數的精度丟失

這個問題鮮有人提及。首先得弄清楚問題是什么:

1. JavaScript 能存儲的最大整數是什么?

該問題前面已回答,是 Number.MAX_VALUE, 非常大的一個數。

2. JavaScript 能存儲的且不丟失精度的最大整數是什么?

根據 s x m x 2^e, 符號位取正,52 位尾數全填充 1, 指數 e 取最大值 971, 顯然,答案依舊是 Number.MAX_VALUE.

我們的問題究竟是什么呢?回到起始代碼:

9999999999999999 == 10000000000000000; // true

很明顯,16 個 9 還遠遠小于 308 個 10. 這個問題與 MAX_VALUE 沒什么關系,還得歸屬到尾數 m 只有 52 位上來。

可以用代碼來描述:

var x = 1; // 為了減少運算量,初始值可以設大一點,比如 Math.pow(2, 53) - 10
while(x != x + 1) x++;
// x = 9007199254740992 即 2^53

也就是說,當 x 小于等于 2^53 時,可以確保 x 的精度不會丟失。當 x 大于 2^53 時,x 的精度有可能會丟失。比如:

x 為 2^53 + 1 時,其二進制表示為:
10000000000...001 (中間共有 52 個 0)
用雙精度浮點數存儲時:
e = 1; m = 10000..00(共 52 個 0,其中 1 是 hidden bit)
顯然,這和 2^53 的存儲是一樣的。

按照上面的思路可以推出,對于 2^53 + 2, 其二進制為 100000…0010(中間 51 個 0),也是可以精確存儲的。

規律:當 x 大于 2^53 且二進制有效位數大于 53 位時,就會存在精度丟失。這和小數的精度丟失本質上是一樣的。

hidden bit 可參考:A tutorial about Java double type.

小結

小數和大整數的精度丟失,并不僅僅在 JavaScript 中存在。嚴格來說,使用了IEEE 754 浮點數格式來存儲浮點類型的任何編程語言(C/C++/C#/Java 等等)都存在精度丟失問題。在 C#、Java 中,提供了 Decimal、BigDecimal 封裝類來進行相應的處理,才避開了精度丟失。

注:ECMAScript 規范中,已有  decimal proposal,但目前尚未被正式采納。

最后考考大家:

Number.MAX_VALUE + 1 == Numer.MAX_VALUE;
Number.MAX_VALUE + 2 == Numer.MAX_VALUE;
...
Number.MAX_VALUE + x == Numer.MAX_VALUE;
Number.MAX_VALUE + x + 1 == Infinity;
...
Number.MAX_VALUE + Number.MAX_VALUE == Infinity;
// 問題:
// 1. x 的值是什么?
// 2. Infinity - Number.MAX_VALUE == x + 1; 是 true 還是 false ?

以上這篇淺談JavaScript中小數和大整數的精度丟失就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支持武林網。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 任丘市| 宝丰县| 五大连池市| 左贡县| 文昌市| 丘北县| 定州市| 运城市| 汕头市| 海伦市| 天长市| 瑞丽市| 军事| 浮山县| 女性| 蒲江县| 久治县| 新晃| 长寿区| 定远县| 克拉玛依市| 会泽县| 大洼县| 库车县| 凤阳县| 茶陵县| 迭部县| 东丽区| 宁城县| 九台市| 五台县| 新闻| 岳池县| 台前县| 烟台市| 沧州市| 安乡县| 本溪| 霞浦县| 嵩明县| 泾源县|