JavaScript Number范围及超出范围运算方法

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/m0_37263637/article/details/81628209

今天在LeetCode上练刀法的时候,遇到了一个问题,两数相加,发现有个边界条件就是输入超过number的范围(大数操作),这就涉及到了知识的盲区,看来刀法还是不锋利,这里总结一下number的问题。

原题目如下:
给定两个非空链表来表示两个非负整数。位数按照逆序方式存储,它们的每个节点只存储单个数字。将两数相加返回一个新的链表。
示例:
输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807

1 JavaScript中Number的范围

JavaScript中所有的数字,无论是整数还是小数,其类型均为Number。在程序内部,Number类型的实质是一个64位的浮点数。(8字节)

1.1 Number范围

数字类型采用64位浮点格式表示,我们可以利用Number对象的属性Number.MAX_VALUE , Number.MIN_VALUE来查看;
JavaScript中Number范围为正负2的53次方,也即从最小值-9007199254740992到最大值+9007199254740992之间的范围。
−9007199254740992 and 9007199254740992 (即正负2的53次方) 11位

1.2 Number在内存中的实现

JavaScript 里的数字是采用 IEEE 754 标准的 64 位双精度浮点数。该规范定义了浮点数的格式,对于64位的浮点数在内存中的表示,最高的1位是符号位,接着的11位是指数,剩下的52位为有效数字,具体:
这里写图片描述

第0位:符号位, s 表示 ,0表示正数,1表示负数;
第1位到第11位:储存指数部分, e 表示 ;
第12位到第63位:储存小数部分(即有效数字),f 表示,
符号位决定了一个数的正负,指数部分决定了数值的大小,小数部分决定了数值的精度。 IEEE 754规定,有效数字第一位默认总是1,不保存在64位浮点数之中。也就是说,有效数字总是1.xx…xx的形式,其中xx..xx的部分保存在64位浮点数之中,最长可能为52位。因此,JavaScript提供的有效数字最长为53个二进制位(64位浮点的后52位+有效数字第一位的1)。

1.3 Number实现对运算带来的问题

http://www.css88.com/archives/7340

1.3.1 Number带来浮点预算精度问题

const float1 =7.9;
const float2 = 0.8;
console.log(float1-float2); //7.1000000000000005

这种问题发生的原因:
首先,十进制的7.9和0.8都会被转换成二进制,但由于浮点数用二进制表达时是无穷的。标准的 64 位双精度浮点数的小数部分最多支持 53 位二进制位,所以浮点数小数位的限制而截断的二进制数字,进行运算后,再转换为十进制,就会产生误差。
这种问题根本原因是在于:计算机都是0或1标识,对于某浮点准确值,是通过不断增加位数去逼近该值。浮点数实现这种方式就是移位运算,所以不管是在表示还是运算时,都会出现误差

如何解决:
1 简单的方法使用toFixed
parseFloat((数学表达式).toFixed(digits)); // toFixed() 精度参数须在 0 与20 之间
// 运行
console.log(parseFloat((float1-float2).toFixed(10)));//7.1
toFixed() 方法可把 Number 四舍五入为指定小数位数的数字。参数为指定小数位数
2 将小数部分单独提取出来作为整数运算然后在拼接,这里我们可以使用封装好的lib。

1.3.2 Number带来大数问题

当我们要计算的值超过了Number最大整数范围时,就会直接报错。
所以如果要进行这样的运算要进行特殊的处理。我自己实现的方式是,将大数的每一位存进数组,然后对两个数组的每一个位单独运算。得到运算结果。

2 解决方案

2.1 使用big-integer处理大数

npm install big-integer

const bigInt = require("big-integer");
let largeNumber1 = bigInt("756435643634734534563423785643879569067365464564324235345");
let largeNumber2 = bigInt("75643564366756753687325768753897357378673698098546456235345");
console.log(largeNumber1);
console.log(largeNumber2);
console.log(bigInt("75643564363473453456342378564387956906736786786546456235345").add("756435643667567536567567873257687538973573786736546456235345"));

返回值为一个object 其中,超过限制的数字被分隔为了数组

{ [Number: 8.32079208031041e+59]
  value:
   [ 2470690,
     2309291,
     3105735,
     5495880,
     5182207,
     239102,
     1040990,
     7920803,
     8320 ],
  sign: false,
  isSmall: false }

其中Number为值,value为分割后的数字数组。

2.2 使用数组处理大数的sample code

function numToArray(num){//仅用于模拟一个由数字组成的数组测试而已
    let NumArray = new Array();
    while(num !== 0){
        let ret = num%10;
        num = Math.floor(num/10) 
        NumArray.push(ret);
    }
    return NumArray;
}
function numBitArrayAdd(nums1, nums2){
    let i = 0;
    let length = nums1.length > nums2.length ? nums1.length : nums2.length;
    let retNums = new Array();
    let addBitFlag = 0;
    let addItem1 = 0;
    let addItem2 = 0;
    let ret = 0;
    for(;(i < length || addBitFlag !== 0); i++){
        addItem1 = nums1[i]  === undefined ? 0 : nums1[i];
        addItem2 = nums2[i]  === undefined ? 0 : nums2[i];
        ret = addItem1 +  addItem2 + addBitFlag;
        if(ret > 9){
            addBitFlag = 1;
            ret = (ret % 10);
        }
        else{
            addBitFlag = 0;
        }
        retNums[i] =ret;
    }
    return retNums;
}

const float1 =7.9;
const float2 = 0.05;
console.log(float1-float2);

console.log("Number MAX:"+ Number.MAX_VALUE);
console.log("Number MIN:"+ Number.MIN_VALUE);
let testNum1 = 134892;
let testNum2 = 2381;
let testArray1 = numToArray(testNum1);
let testArray2 = numToArray(testNum2);
let resArray = numBitArrayAdd(testArray1, testArray2);
console.log(testArray1);
console.log(testArray2);
console.info(resArray);
console.info(resArray.reverse());
console.info(testNum1+testNum2);

猜你喜欢

转载自blog.csdn.net/m0_37263637/article/details/81628209