大数相加

使用 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 混合运算,需要显式转换
  • 字符串方法是面试重点,考察对进位逻辑的理解