程序员

js大数相加问题

2018-11-01  本文已影响0人  鱼仔1234

一般情况下用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的响应最佳。
各种方法各有利弊,在不确定参数的形式的时候,应该要谨慎使用,做好类型判断,防止程序报错

上一篇下一篇

猜你喜欢

热点阅读