var number1 = 10000000000000000000000000 + 11111111111111111111111111 //理論上number1的值應該是21111111111111111111111111(javascript中會表示爲科學計數法:2.111111111111111e+25)
var number2 = 21111111111111111111111000
console.log(number1 === number2) //true
複製代碼
這個不用算簡單看一下都知道計算結果不對,最後幾位確定應該都是1的,但是爲何會獲得一個不對的值呢?javascript
由於JavaScript
的Number
類型是遵循IEEE 754規範表示的,這就意味着JavaScript
能精確表示的數字是有限的,JavaScript
能夠精確到個位的最大整數是9007199254740992,也就是2的53次方,超過這個範圍就會精度丟失,形成JavaScript
沒法判斷大小,從而會出現下面的現象:php
Math.pow(2, 53); // 9007199254740992
Math.pow(2, 53) === Math.pow(2, 53) + 1; // true
9007199254740992 === 9007199254740992 + 1; // true
複製代碼
能夠從下面這張圖上看到JavaScript
Number
可以精確表示的上下限: html
那當兩個數據相加時,其中一個或者兩個數據都超過了這個精度範圍,直接相加結果就會不許了,那怎麼解決呢?java
參考網上經常使用的一中方案是將Number
轉爲String
,而後將String
轉爲Array
,而且注意補齊較短的數組,將他們的長度標稱同樣再一一相加獲得一個新數組,再講和組成的新數組轉爲數字就能夠了,下面是實現代碼:數組
function sumString(a, b) {
a = '0' + a;
b = '0' + b; //加'0'首先是爲了轉爲字符串,並且兩個數相加後可能須要進位,這樣保證了和的長度就是a、b中長的那個字符的長度
var arrA = a.split(''), //將字符串轉爲數組
arrB = b.split(''),
res = [], //相加結果組成的數組
temp = '', //相同位數相加的值
carry = 0, //同位數相加結果大於等於10時爲1,不然爲0
distance = a.length - b.length, //計算兩個數字字符串的長度差
len = distance > 0 ? a.length : b.length; //和的長度
// 在長度小的那個值前加distance個0,保證兩個數相加以前長度是想等的
if(distance > 0) {
for(let i = 0; i < distance; i++) {
arrB.unShift('0');
}
}else{
for(let i = 0; i < distance; i++) {
arrA.unShift('0');
}
}
// 如今獲得了兩個長度一致的數組,須要作的就是把他們想通位數的值相加,大於等於10的要進一
// 最終獲得一個和組成的數組,將數組轉爲字符串,去掉前面多餘的0就獲得了最終的和
for(let i = len-1; i >= 0; i--) {
temp = Number(arrA[i]) + Number(arrB[i]) + carry;
if(temp >= 10) {
carry = 1;
res.unshift((temp + '')[1])
}
else{
carry = 0;
res.unshift(temp)
}
}
res = res.join('').replace(/^0/, '');
console.log(res);
}
複製代碼
前面講到,在JavaScript
中,使用浮點數標準IEEE 754
表示數字的,在表示小數的時候,在轉化二進制的時候有些數是不能完整轉化的,好比0.3,轉化成二進制是一個很長的循環的數,是超過了JavaScript
能表示的範圍的,因此近似等於0.30000000000000004。瀏覽器
這個是二進制浮點數最大的問題(不只 JavaScript,全部遵循 IEEE 754 規範的語言都是如此)。bash
在這裏咱們要引入ES6中在Number
對象上新增的一個極小的常量Number.EPSILON
。它表示1與大於1的最小浮點數之差,等於2的-52次方。學習
Number.EPSILON其實是 JavaScript 可以表示的最小精度。偏差若是小於這個值,就能夠認爲已經沒有意義了,即不存在偏差了。ui
因此能夠用這個來判斷兩個數浮點數是否想等:spa
function numIsEqual(lef, rig) {
return Math.abs(lef - rig) < Number.EPSILON
}
複製代碼
若是考慮瀏覽器兼容的話能夠這樣寫:
function numIsEqual(lef, rig) {
let EPSILON = Number.EPSILON ? Number.EPSILON : Math.pow(2,-52)
return Math.abs(lef - rig) < EPSILON
}
複製代碼
固然咱們平常使用小數的話通常精確到小數點後兩位就夠了,是不會有太大的問題的,可是必定要記得用toFied()
方法保留小數點後位數,不能直接取計算的值,避免偏差。
關於JavaScript
中數據的表示問題,我以前在極客時間的《深刻淺出計算機組成原理》中還聽到過一些,那裏面詳細講解了爲何浮點數的IEEE 754
標準只能表示有限的數字,不過對於這麼底層的東西我還只能勉強意會,哈哈。