JavaScript 採用 IEEE754標準 中的浮點數算法來表示數字 Number。html
我也沒花時間去詳細瞭解 IEEE754標準 ,但對於處理超大整數,瞭解下面的幾個知識點就足夠了。算法
首先,JavaScript 實際上能夠表示的最大數是: 1.7976931348623157e+308app
Number.MAX_VALUE; // 1.7976931348623157e+308
雖然這個數能夠正確表示出來,但會存在「精度丟失」的問題。測試
那什麼是「精度丟失」? 咱們看看下面的例子:this
num1 = 10000000000000000000000000 + 11111111111111111111111111; // 2.111111111111111e+25 num2 = 21111111111111111111111000; // 2.111111111111111e+25 num1 === num2; // true
按照常規的數學預算, num1 的計算結果是 21111111111111111111111111,而 num2 的值是 21111111111111111111111000,二者是不可能相等。但實際上 JavaScript 能夠精確表示到個位的最大整數是:9007199254740992spa
Math.pow(2, 53); // 9007199254740992 Math.pow(2, 53) === Math.pow(2, 53) + 1; // true 9007199254740992 === 9007199254740992 + 1; // true
關於 JavaScript Number 的一些上下極限,更詳細的資料能夠看下圖:prototype
正由於 JavaScript 的 Number 類型存在這些限制,當咱們須要處理兩個「超大整數」的相加時,直接套用加法運算符會存在如下問題:code
爲了解決這些問題,才產生了「超大整數」加法的需求,實現代碼以下:orm
var largeIntegerAddition = function () { function isNumberString() { var result = true; for (var i = arguments.length; i--;) { if (typeof arguments[i] !== 'string' || !/^\d+$/.test(arguments[i])) { console.error('arguments format is incorrect!'); result = false; break; } } return result; } function trimHeadZero(numberStr) { return numberStr.replace(/^0*/, ''); } return function () { var bigNum1 = arguments[0], bigNum2 = arguments[1]; if (!bigNum2) { return isNumberString(bigNum1) ? trimHeadZero(bigNum1) : '0'; } else { if (!isNumberString(bigNum1, bigNum2)) { return '0'; } bigNum1 = trimHeadZero(bigNum1); bigNum2 = trimHeadZero(bigNum2); var carry = 0, // 進位 bigNum1Split = bigNum1.split('').reverse(), bigNum2Split = bigNum2.split('').reverse(), result = '', maxNumSize = bigNum1Split.length > bigNum2Split.length ? bigNum1Split.length : bigNum2Split.length; for (var i = 0; i < maxNumSize; i++) { var n1 = bigNum1Split[i] ? +bigNum1Split[i] : 0, n2 = bigNum2Split[i] ? +bigNum2Split[i] : 0, sum = (n1 + n2 + carry).toString(); if (sum.length > 1) { carry = +sum.slice(0, 1); result = sum.slice(1, 2) + result; } else { carry = 0; result = sum + result; } } if (carry !== 0) { result = carry + result; } if (arguments[2]) { var argumentArr = Array.prototype.slice.call(arguments, 0).slice(2); argumentArr.unshift(result); return largeIntegerAddition.apply(this, argumentArr); } else { return result; } } } }();
測試用例:htm
// 測試用例 function unitTest(arg, result) { var res = largeIntegerAddition.apply(this, arg); console.log(res, res === result); } unitTest([], ''); unitTest(['012', 3], '15'); unitTest(['012', '0013', '214', 100002], '100241'); unitTest(['1.1111111111111111e+227', '1'], '1.1111111111111111e+227'); unitTest(['123'], '123'); unitTest(['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'], '45'); unitTest(['0', '2', '3', '4', '123'], '132'); unitTest(['012', '3'], '15'); unitTest(['012', '0013', '214', '100002'], '100241'); unitTest(['99999999999999999999', '1'], '100000000000000000000'); unitTest(['99999999999999999999', '11111111111111111111'], '111111111111111111110'); unitTest(['99999999999999999999', '11111111111111111111', '11111111'], '111111111111122222221'); unitTest(['4810284728175829182', '92817475910285750182'], '97627760638461579364'); unitTest(['4810284728175829182', '92817475910285750182', '9728172845'], '97627760648189752209'); unitTest(['4810284728175829182', '92817475910285750182', '9728172845' , '92875018002020102'], '97720635666191772311'); unitTest([ (function () { var str = ''; for (var i = 500; i--;) { str += '9'; } return str; })(), (function () { var str = ''; for (var i = 500; i--;) { str += '1'; } return str; })() ], (function () { var str = ''; for (var i = 500; i--;) { str += '1'; } return str + '0'; })());
本文做者:Maple Jan