js大数相加问题

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,036评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,046评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,411评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,622评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,661评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,521评论 1 304
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,288评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,200评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,644评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,837评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,953评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,673评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,281评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,889评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,011评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,119评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,901评论 2 355

推荐阅读更多精彩内容