一般情况下用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即以内的整数都是精确的,但是超过了这个范围就会出现精度丢失
怎么计算这种大数的相加呢?
解决方法的思路是以字符串的形式按位来相加,即我们平时计算加法一样,个位与个位相加,超过十就进一位的思路
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的响应最佳。
各种方法各有利弊,在不确定参数的形式的时候,应该要谨慎使用,做好类型判断,防止程序报错