Skip to main content

浮點數陷阱 (Floating Point Trap)

大部分語言的數字(像 JavaScript 的 Number 型別)都是採用 IEEE 754 雙精度浮點數 (64-bit double precision floating point) 來表示。
這會帶來一些陷阱:

1. 精度問題

  • 有些小數無法被精確表示,例如:

    console.log(0.1 + 0.2); // 0.30000000000000004

    因為二進位制表示時,0.10.2 都是無窮小數,只能近似存下來。

2. 大數不精確

  • JavaScript 的 Number 安全整數範圍是 -(2^53 - 1) ~ 2^53 - 1

  • 超過這個範圍,整數計算會出錯:

    console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991
    console.log(9007199254740991 + 1); // 9007199254740992 ✅
    console.log(9007199254740991 + 2); // 9007199254740992 ❌

3. 比較陷阱

  • 浮點數計算後結果常常不是精確值,導致比較出錯:

    console.log(0.1 + 0.2 === 0.3); // false
  • 一般建議比較時使用「容差」(epsilon):

    function isEqual(a, b) {
    return Math.abs(a - b) < Number.EPSILON;
    }
    console.log(isEqual(0.1 + 0.2, 0.3)); // true

BigInt 型別

為了解決 大數精度問題,ES2020 引入了 BigInt 型別。

  1. 定義

    • 在數字後面加 n,或用 BigInt() 產生:

      const big1 = 123456789012345678901234567890n;
      const big2 = BigInt("9007199254740993");
  2. 範圍

    • BigInt 沒有理論上的最大值或最小值(只受記憶體限制),比 Number 精確許多。
  3. 運算

    • 可做加減乘除,但 不能直接和 Number 混用

      const a = 10n;
      const b = 20n;
      console.log(a + b); // 30n

      const c = 10;
      // console.log(a + c); // ❌ TypeError: Cannot mix BigInt and other types
      console.log(a + BigInt(c)); // ✅ 20n
  4. 除法

    • BigInt 除法會直接「取整數」:

      console.log(7n / 2n); // 3n (沒有小數部分)
  5. 使用情境

    • 金融計算(避免浮點誤差)。

    • 密碼學、大數演算法(超過 2^53 的整數)。

    • ID 系統(像資料庫的 long 型別)。


總結

  • 浮點數陷阱 → 小數計算可能不準,大整數會超出安全範圍。

  • BigInt → 解決大數計算精度問題,但不支援小數。

  • 實務做法

    • 金融應用(需要小數點精確) → 用字串處理或 decimal.js 等庫。

    • 大整數(ID、演算法) → 用 BigInt

    • 一般數值 → 用 Number