在 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] ✅
解法步驟:
- 用
digits.join("")
將陣列轉為字串 - 用
BigInt()
轉換為大整數 - 加上
1n
(BigInt 型別的 1) - 轉回字串再拆分成數字陣列
總結
BigInt
為 JavaScript 帶來了處理大整數的能力,解決了 Number
型別的精度限制。
另外,雖然我們用 BigInt 優雅解決了 LeetCode 這題,但這題的本質是想考進位處理演算法。
所以,如果你是為了練演算法,不妨再動手實作一次進位邏輯。