一、业务场景
需要将两个 id 数值相加,得到联合主键,或者将两个正整数相加,求和。
二、遇到的问题
刚开始直接使用Number(id1) + Number(id2),得到的数据不正确。
function numberAdd(id1, id1) {
return Number(id1) + Number(id2);
}
console.log(numberAdd('111111111111111111', '1'));
// 得到错误的结果:111111111111111100。无法正确求和得到111111111111111112。
三、原因
因为 Javascript 的数字存储使用了 IEEE 754 中规定的双精度浮点数数据类型,而这一数据类型能够安全存储 -(2^53 - 1) 到 2^53 - 1 之间的数值(包含边界值)。
Number.MAX_SAFE_INTEGER 常量表示在 JavaScript 中最大的安全整数,是一个值为 9007199254740991 的常量。
例如:
Number.MAX_SAFE_INTEGER + 1 === Number.MAX_SAFE_INTEGER + 2 // 会得到true
四、解决办法
1、使用 BigInt
BigInt 是一种内置对象,它提供了一种方法来表示大于 2^53 - 1 的整数。
可以用在一个整数字面量后面加 n 的方式定义一个 BigInt ,如:10n;或者调用函数 BigInt()并传递一个整数值或字符串值。
注意:不能和任何 Number 实例混合运算,两者必须转换成同一种类型。
警告:当使用 BigInt 时,带小数的运算会被取整。
const rounded = 5n / 2n; // 得到2n, 而不是2.5n
大数相加
function bigIntAdd(id1, id2) {
return String(BigInt(id1) + BigInt(id2));
}
console.log(bigIntAdd('111111111111111111', '1')); // 得到正确结果:111111111111111112
2、字符串相加算法
BigInt是在ES2020中引入的新特性。还可以自己实现大数相加的算法。
function addStrings (str1, str2) {
let maxLength = Math.max(str1.length, str2.length); // 获取两个数字的最大长度
str1 = str1.padStart(maxLength, 0); // 用0补齐长度,让它们两个长度相同
str2 = str2.padStart(maxLength, 0);
let temp = 0; // 每个位置相加之和
let flag = 0; // 进位:相加之和如果大于等于10,则需要进位
let result = ""; // 最终结果返回值
for(let i = maxLength-1; i >= 0; i--) {
temp = parseInt(str1[i]) + parseInt(str2[i]) + flag; // 获取当前位置的相加之和:字符串1 + 字符串2 + 进位数字
flag = Math.floor(temp/10); // 获取下一个进位
result = temp % 10 + result; // 拼接结果字符串
}
if(flag === 1) { // 如果遍历完成后,flag还剩1,说明两数相加之后多了一位,类似于:95 + 10 = 105
result = "1" + result;
}
return result;
};
console.log(addStrings('111111111111111111', '1')); // 得到正确结果:111111111111111112