大数相加
使用 BigInt 和字符串模拟实现大数相加
问题
JavaScript 中 Number 类型有精度限制,超过 Number.MAX_SAFE_INTEGER(2^53 - 1)会丢失精度。实现一个函数,能够正确计算两个大数的和。
// Number 精度丢失示例
console.log(9007199254740992 + 1); // 9007199254740992 ❌
console.log(9007199254740992 + 2); // 9007199254740994 ❌
解答
方法一:使用 BigInt
function addBigInt(a, b) {
// 将字符串转为 BigInt,相加后转回字符串
return (BigInt(a) + BigInt(b)).toString();
}
// 测试
console.log(addBigInt('9007199254740992', '1')); // '9007199254740993'
console.log(addBigInt('123456789012345678901234567890', '987654321098765432109876543210'));
// '1111111110111111111011111111100'
方法二:字符串模拟(手写实现)
function addStrings(num1, num2) {
let i = num1.length - 1;
let j = num2.length - 1;
let carry = 0; // 进位
let result = '';
// 从末位开始逐位相加
while (i >= 0 || j >= 0 || carry) {
const n1 = i >= 0 ? parseInt(num1[i]) : 0;
const n2 = j >= 0 ? parseInt(num2[j]) : 0;
const sum = n1 + n2 + carry;
carry = Math.floor(sum / 10); // 计算进位
result = (sum % 10) + result; // 当前位结果
i--;
j--;
}
return result;
}
// 测试
console.log(addStrings('123', '456')); // '579'
console.log(addStrings('9999', '1')); // '10000'
console.log(addStrings('9007199254740992', '1')); // '9007199254740993'
处理负数的完整版本
function addBigNumbers(num1, num2) {
// 判断正负
const isNeg1 = num1[0] === '-';
const isNeg2 = num2[0] === '-';
// 去掉符号
const abs1 = isNeg1 ? num1.slice(1) : num1;
const abs2 = isNeg2 ? num2.slice(1) : num2;
// 同号相加
if (isNeg1 === isNeg2) {
const sum = addStrings(abs1, abs2);
return isNeg1 ? '-' + sum : sum;
}
// 异号相减
const cmp = compare(abs1, abs2);
if (cmp === 0) return '0';
const [bigger, smaller] = cmp > 0 ? [abs1, abs2] : [abs2, abs1];
const diff = subtractStrings(bigger, smaller);
// 确定结果符号
const resultNeg = cmp > 0 ? isNeg1 : isNeg2;
return resultNeg ? '-' + diff : diff;
}
// 比较两个正数字符串大小
function compare(a, b) {
if (a.length !== b.length) return a.length - b.length;
return a.localeCompare(b);
}
// 大数减小数(保证 a >= b)
function subtractStrings(a, b) {
let i = a.length - 1;
let j = b.length - 1;
let borrow = 0;
let result = '';
while (i >= 0) {
let diff = parseInt(a[i]) - (j >= 0 ? parseInt(b[j]) : 0) - borrow;
if (diff < 0) {
diff += 10;
borrow = 1;
} else {
borrow = 0;
}
result = diff + result;
i--;
j--;
}
// 去掉前导零
return result.replace(/^0+/, '') || '0';
}
关键点
- BigInt 是 ES2020 新增类型,字面量用
n后缀(如123n),或用BigInt()构造 - 字符串模拟:从末位开始逐位相加,注意处理进位和长度不等的情况
- 循环条件
i >= 0 || j >= 0 || carry:确保处理完所有位和最后的进位 - BigInt 不能与 Number 混合运算,需要显式转换
- 字符串方法是面试重点,考察对进位逻辑的理解
目录