JavaScript BigInt:優雅解決大數字精度問題

在 JavaScript 開發中,處理大數字時你可能會遇到一個隱藏陷阱:精度丟失。今天我們來聊聊這個問題,以及 BigInt 如何優雅地解決它。

問題背景:Number 的精度限制

為什麼會有精度問題?

JavaScript 的 Number 型別基於 IEEE 754 雙精度浮點數標準,其能夠精確表示的整數範圍有限:

// 安全整數範圍
console.log(Number.MAX_SAFE_INTEGER); // 9,007,199,254,740,991 (2^53 - 1)
console.log(Number.MIN_SAFE_INTEGER); // -9,007,199,254,740,991 (-(2^53 - 1))

精度丟失的實際案例

超過安全範圍的數字運算會出現精度丟失:

// 精度丟失範例
console.log(9007199254740993); // 預期:9007199254740993
// 實際:9007199254740992 ❌ 精度丟失

console.log(150121231230120301); // 預期:150121231230120301
// 實際:150121231230120300 ❌ 精度丟失

BigInt

什麼是 BigInt?

BigInt 是 ES2020 (ES11) 引入的新原始型別,專門用於處理任意精度的整數,完全不受 Number.MAX_SAFE_INTEGER 限制。

建立 BigInt

// 方法一:字面值語法(推薦)
const bigInt1 = 123n;

// 方法二:BigInt 建構函數
const bigInt2 = BigInt("123456789012345678901234567890");
const bigInt3 = BigInt(123);

基本運算

const a = 123n;
const b = 456n;

console.log(a + b); // 579n
console.log(a * b); // 56088n
console.log(a - b); // -333n
console.log(b / a); // 3n(整數除法)

重要限制

⚠️ BigInt 無法與 Number 直接混用

// ❌ 錯誤用法
1n + 1; // TypeError: Cannot mix BigInt and other types

// ✅ 正確用法
1n + 1n; // 2n
Number(1n) + 1; // 2

實戰應用:LeetCode Plus One

題目描述

給定一個由數字組成的陣列 digits,代表一個大整數。將此整數加 1 後,回傳結果陣列。

範例:

// 範例 1
輸入: [1,2,3]
輸出: [1,2,4]
解釋: 123 + 1 = 124

// 範例 2
輸入: [9]
輸出: [1,0]
解釋: 9 + 1 = 10

傳統解法的問題

最直覺的想法可能是這樣寫:

function plusOne(digits) {
  let number = Number(digits.join("")) + 1;
  return number
    .toString()
    .split("")
    .map((v) => Number(v));
}

看起來沒什麼問題,不過就是將陣列轉成數字加 1,然後再轉回去。

但送出後就會發現,咦!竟然失敗了:

plusOne([6, 1, 4, 5, 3, 9, 0, 1, 9, 5, 1, 8, 6, 7, 0, 5, 5, 4, 3]);
// 預期: [6,1,4,5,3,9,0,1,9,5,1,8,6,7,0,5,5,4,4]
// 實際: [6,1,4,5,3,9,0,1,9,5,1,8,6,7,0,5,0,0,0] 💥

debug 後就會發現 6145390195186705543 + 1 會變成 6145390195186705000
這,就是精度問題在作怪!

BigInt 解法

使用 BigInt 可以優雅解決這個問題:

function plusOne(digits) {
  const number = BigInt(digits.join("")) + 1n;
  return number
    .toString()
    .split("")
    .map((v) => Number(v));
}

// 測試結果
console.log(plusOne([6, 1, 4, 5, 3, 9, 0, 1, 9, 5, 1, 8, 6, 7, 0, 5, 5, 4, 3]));
// 輸出: [6,1,4,5,3,9,0,1,9,5,1,8,6,7,0,5,5,4,4] ✅

解法步驟:

  1. digits.join("") 將陣列轉為字串
  2. BigInt() 轉換為大整數
  3. 加上 1n(BigInt 型別的 1)
  4. 轉回字串再拆分成數字陣列

總結

BigInt 為 JavaScript 帶來了處理大整數的能力,解決了 Number 型別的精度限制。
另外,雖然我們用 BigInt 優雅解決了 LeetCode 這題,但這題的本質是想考進位處理演算法。
所以,如果你是為了練演算法,不妨再動手實作一次進位邏輯。

參考資料

Similar Posts