JavaScript超大整數加法

 

什麼是「超大整數」?

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

  • 當結果大於 Math.pow(2, 53)  時,會出現精度丟失,致使最終結果存在誤差
  • 當結果大於 Number.MAX_VALUE,直接返回 Infinity

 

爲了解決這些問題,才產生了「超大整數」加法的需求,實現代碼以下: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

本文連接:http://www.cnblogs.com/maplejan/p/3893545.html

相關文章
相關標籤/搜索