当前位置: 首页 > news >正文

JavaScript超大整数加法

原文: JavaScript超大整数加法

 

什么是「超大整数」?

JavaScript 采用 IEEE754标准 中的浮点数算法来表示数字 Number。

我也没花时间去详细了解 IEEE754标准 ,但对于处理超大整数,了解下面的几个知识点就足够了。

首先,JavaScript 实际上可以表示的最大数是: 1.7976931348623157e+308

Number.MAX_VALUE;    // 1.7976931348623157e+308

虽然这个数可以正确表示出来,但会存在「精度丢失」的问题。

那什么是「精度丢失」? 我们看看下面的例子:

num1 = 10000000000000000000000000 + 11111111111111111111111111;    // 2.111111111111111e+25
num2 = 21111111111111111111111000;    // 2.111111111111111e+25
num1 === num2;    // true

按照常规的数学预算, num1 的计算结果是 21111111111111111111111111,而 num2 的值是 21111111111111111111111000,两者是不可能相等。但实际上 JavaScript 可以精确表示到个位的最大整数是:9007199254740992

Math.pow(2, 53);    // 9007199254740992
Math.pow(2, 53) === Math.pow(2, 53) + 1;    // true
9007199254740992 === 9007199254740992 + 1;    // true

关于 JavaScript Number 的一些上下极限,更详细的资料可以看下图:

正因为 JavaScript 的 Number 类型存在这些限制,当我们需要处理两个「超大整数」的相加时,直接套用加法运算符会存在以下问题:

  • 当结果大于 Math.pow(2, 53)  时,会出现精度丢失,导致最终结果存在偏差
  • 当结果大于 Number.MAX_VALUE,直接返回 Infinity

 

为了解决这些问题,才产生了「超大整数」加法的需求,实现代码如下:

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;
            }
        }
    }
}();

 

测试用例:

// 测试用例
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

 

相关文章:

  • 多重和嵌套if
  • 曾国藩《挺经》卷七英才
  • OC关于NSDate类的方法和应用的总结!(全)
  • LIS问题---HDU1025 Constructing Roads In JGShining's Kingdom
  • xcode的ios工程目录结构复习
  • 基于运动特征的视频质量评价方法(基于H.264)
  • lvm的使用总结
  • C# 使用int.TryParse,Convert.ToInt32,(int)将浮点类型转换整数时的区别
  • 中国电信如何转型? ——论政企客户的需求转变
  • Oracle11g RAC常用操作 (维护及管理)
  • 【声明】请原谅我文章过短
  • SDH、MSTP、OTN和PTN的区别和联系
  • Class.forName(ClassName)与ClassName.class的区别
  • JAVA深入研究——Method的Invoke方法。
  • 【wikioi】1227 方格取数 2(费用流)
  • 5分钟即可掌握的前端高效利器:JavaScript 策略模式
  • Idea+maven+scala构建包并在spark on yarn 运行
  • java 多线程基础, 我觉得还是有必要看看的
  • javascript从右向左截取指定位数字符的3种方法
  • Javascript基础之Array数组API
  • javascript数组去重/查找/插入/删除
  • JDK9: 集成 Jshell 和 Maven 项目.
  • niucms就是以城市为分割单位,在上面 小区/乡村/同城论坛+58+团购
  • node学习系列之简单文件上传
  • Redux 中间件分析
  • Unix命令
  • 从tcpdump抓包看TCP/IP协议
  • 从零到一:用Phaser.js写意地开发小游戏(Chapter 3 - 加载游戏资源)
  • 基于Volley网络库实现加载多种网络图片(包括GIF动态图片、圆形图片、普通图片)...
  • 计算机常识 - 收藏集 - 掘金
  • 网页视频流m3u8/ts视频下载
  • 小程序滚动组件,左边导航栏与右边内容联动效果实现
  • 要让cordova项目适配iphoneX + ios11.4,总共要几步?三步
  • 在Docker Swarm上部署Apache Storm:第1部分
  • 深度学习之轻量级神经网络在TWS蓝牙音频处理器上的部署
  • ​软考-高级-系统架构设计师教程(清华第2版)【第1章-绪论-思维导图】​
  • # include “ “ 和 # include < >两者的区别
  • #if 1...#endif
  • #pragma pack(1)
  • (52)只出现一次的数字III
  • (ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY)讲解
  • (二十一)devops持续集成开发——使用jenkins的Docker Pipeline插件完成docker项目的pipeline流水线发布
  • (分布式缓存)Redis分片集群
  • (附源码)ssm高校志愿者服务系统 毕业设计 011648
  • (六)什么是Vite——热更新时vite、webpack做了什么
  • (转) RFS+AutoItLibrary测试web对话框
  • (转) SpringBoot:使用spring-boot-devtools进行热部署以及不生效的问题解决
  • (转)Spring4.2.5+Hibernate4.3.11+Struts1.3.8集成方案一
  • (转载)跟我一起学习VIM - The Life Changing Editor
  • ***测试-HTTP方法
  • .NET Core使用NPOI导出复杂,美观的Excel详解
  • .net framwork4.6操作MySQL报错Character set ‘utf8mb3‘ is not supported 解决方法
  • .NET 除了用 Task 之外,如何自己写一个可以 await 的对象?
  • .Net程序帮助文档制作
  • .Net调用Java编写的WebServices返回值为Null的解决方法(SoapUI工具测试有返回值)