美文网首页程序员
js大数相加问题

js大数相加问题

作者: 鱼仔1234 | 来源:发表于2018-11-01 15:55 被阅读0次

    一般情况下用js写一个两个数相加的函数很简单,如下:

    function sum(a, b) {
      return Number(a) + Number(b)
    }
    
    console.log(sum(1, 3))
    

    我们在控制台运行一下,发现打印出了4,没有问题

    但是我们改一下代码

    function sum(a, b) {
      return Number(a) + Number(b)
    }
    
    console.log(sum(11111111111111111, 11111111111111111))
    

    结果按理说应该是 22222222222222222,但是运行后发现 控制台输出的是 22222222222222224

    那我们少加点

    console.log(sum(9007199254740991, 1))

    发现结果是 9007199254740992

    console.log(sum(9007199254740992, 1))

    发现结果是 9007199254740992

    这会可能已经发现问题所在了,因为 js的 Number 是IEEE 754标准的64-bits的双精度数值

    就是说在 2^53即以内的整数都是精确的,但是超过了这个范围就会出现精度丢失


    1.png

    怎么计算这种大数的相加呢?

    解决方法的思路是以字符串的形式按位来相加,即我们平时计算加法一样,个位与个位相加,超过十就进一位的思路

    function bigSum(a, b) {
      // 已 12345 和 678 为例
      // 我们需要先把他们转换为位数相同,不够补零,记住要统一加一位,为了两个最大的位数相加后可能需要进位
      // 12345 =>  012345    678 => 000678
      // 然后让各自的个位个位相加,十位与十位相加   5 + 8 = 3  (1为进位) 4 + 7 + 1 = 2 (1) .....
      a = '0' + a 
      b = '0' + b
      let aArr = a.split('')
      let bArr = b.split('')
      let carry = 0
      let res = []
      let length = Math.max(aArr.length,bArr.length)
      let distance = aArr.length - bArr.length
      if (distance > 0) {
        for(let i = 0; i < distance; i++){
          bArr.unshift('0');
        }
      } else{
        for(let i = 0; i < Math.abs(distance); i++){
          aArr.unshift('0');
        }
      }
      for(let i = length - 1; i >= 0; i--) {
        let sum = Number(aArr[i]) + Number(bArr[i]) + Number(carry)
        carry = sum > 10 ? 1 : 0
        sum = sum > 10 ? parseInt(sum / 10) : sum
        res.unshift(sum)
      }
      return res.join('').replace(/^0/,'')
    }
    console.log(bigSum('9007199254740993', '1'))
    // 注意: 传参时就需传入字符串,如果是数字类,在传参时就已经出现精度丢失
    

    参考

    更简洁的方法

    function sumStrings(a,b){
      var res='', c=0;
      a = a.split('');
      b = b.split('');
      while (a.length || b.length || c){
          c += ~~a.pop() + ~~b.pop();
          res = c % 10 + res;
          c = c>9;
      }
      return res.replace(/^0+/,'');
     
    }
    

    res 保存了结果,c保存按位加的结果及进位
    这里的~运算符是按位非 ~0 = -1 ~~0 = 1 ~~true = 1

    按位非~及其他几种字符串转数字方法

    parseInt

    parseInt('012')      ----    12
    parseInt('12')       ----    12
    parseInt('12abc')    ----    12
    parseInt('12.4')     ----    12
    

    parseFloat

    parseFloat('1.4')    ----    1.4
    parseFloat('14')     ----    14
    parseFloat('1.4ab')  ----    1.4
    

    ~~

    ~~1.23               ----    1
    ~~'1.23'             ----    1
    ~~'abc'              ----    0
    

    Number

    Number("023")        ----    23
    Number("02.3")       ----    2.3
    Number("avx")        ----    NaN
    

    根据JsPerf.com的基准测试 Number是JsPerf中最慢的之一 大多数浏览器对parseInt的响应最佳。
    各种方法各有利弊,在不确定参数的形式的时候,应该要谨慎使用,做好类型判断,防止程序报错

    相关文章

      网友评论

        本文标题:js大数相加问题

        本文链接:https://www.haomeiwen.com/subject/qtitxqtx.html