首頁
學習紀錄
遊戲心得影視Life書單案件檔案
Side Projects委託作品與二創互動實驗場
Kurau
百百 BLOG
首頁
學習紀錄
遊戲心得影視Life書單案件檔案
Side Projects委託作品與二創互動實驗場
Kurau

Kurau Blog

「隨心而寫,真真假假,都是我」

一個記錄生活、輸出興趣的個人空間。
遊戲、影視、閱讀、學習……每一段體驗都值得留下文字。

頁面導覽

  • 學習紀錄
  • 遊戲心得
  • 影視Life
  • 書單
  • 委託作品與二創
  • Kurau
  • 合作邀請

找到我

歡迎來 Discord 找我聊天!

“曾經發生的事不可能忘記,只是暫時想不起來而已。”-《神隱少女》

© 2026 Kurau All rights reserved

程式語言

JavaScript 浮點數問題

By Kurau·2023-03-08·Updated 2026-05-09·6 分鐘閱讀

JavaScript 浮點數精度問題

TL;DR
0.1 + 0.2 !== 0.3 不是 JS bug,是 IEEE 754 浮點數標準 的本質限制。所有走 IEEE 754 的語言都有這問題。金錢計算永遠用 Decimal.js / BigInt + 整數位元==,別用 Number;簡單顯示用 toFixed()。

經典範例(全部踩過)

0.1 + 0.2 === 0.3                  // false ⚠️
0.1 + 0.2                          // 0.30000000000000004
0.1 * 3                            // 0.30000000000000004
19.9 * 100                         // 1989.9999999999998

(0.1 + 0.2 + 0.3) === 0.6          // false
0.3 - 0.2                          // 0.09999999999999998
javascript

為什麼:電腦用 二進位浮點數 表示小數,0.1 在二進位是 無限循環(0.0001100110011...),只能截斷儲存 → 失準。

0.1 (10 進位) = 0.0001100110011001100110011001100110011001100110011...(2 進位,無限循環)
                                            ↑
                                  IEEE 754 截斷在這附近

4 種解法

1. toFixed()(簡單顯示用)

+(0.1 + 0.2).toFixed(2)                 // 0.3
(0.1 + 0.2).toFixed(2)                  // "0.30" (string!)
javascript

注意:toFixed 回傳 string,前面加 + 或 Number() 轉回 number。

適合:UI 顯示金額、簡單比較、低精度需求 不適合:多次累積運算(精度誤差會累積)

2. 先乘後除(整數運算)

(0.1 * 10 + 0.2 * 10) / 10              // 0.3 ✅
javascript

原理:把 float 轉成 整數運算(整數沒精度問題),最後再除回去。

陷阱:乘法本身也可能爆:0.1 * 10 的結果你以為是 1,但...

0.1 * 10                                // 1 ✅(湊巧 OK)
0.29 * 100                              // 28.999999999999996 ⚠️
javascript

所以只在 1-2 位小數 時可靠,複雜情況仍要用 Decimal.js。

3. Decimal.js / Big.js(推薦)

npm install decimal.js
bash
import Decimal from 'decimal.js';

new Decimal(0.1).plus(0.2).toNumber()           // 0.3 ✅
new Decimal('0.1').plus('0.2').toString()       // '0.3'

// 鏈式運算
new Decimal(100).times(0.29).toNumber()         // 29 ✅
javascript

選擇:

套件大小特色
decimal.js~30KB任意精度、一般場景首選
decimal.js-light~12KB精簡版
big.js~6KB最輕,API 較陽春
bignumber.js~30KB跟 decimal.js 同作者,API 略不同
金融計算強制用
任何處理金錢、稅務、計費的程式碼,別用 Number,直接 Decimal.js。一次省 N 次客訴。

4. Number.EPSILON(僅用於比較)

function isFloatEqual(a, b) {
  return Math.abs(a - b) < Number.EPSILON;
}

isFloatEqual(0.1 + 0.2, 0.3)            // true ✅
javascript

Number.EPSILON ≈ 2.22e-16,是 Number 的最小可分辨差距。只解決「比較」,不解決運算後的儲存值仍然是 0.30000...004。


進階解法:整數位元儲存

銀行 / 券商 / 大型電商常用:

// 用「分」當單位,避免小數
const priceInCents = 1099;              // $10.99
const taxInCents = 88;
const totalCents = priceInCents + taxInCents;     // 1187
const display = (totalCents / 100).toFixed(2);    // "11.87"
javascript

規則:

  • DB 存整數(以最小單位 — 分 / 厘)
  • 所有運算 都用整數
  • 只在 顯示時 除回小數

特別重要:對接金融 API(Stripe / 台灣金流)規範就是用最小單位,你的內部結構配合最省事。

// Stripe API 的 amount 是 cents
stripe.charges.create({
  amount: 2000,       // $20.00 = 2000 cents
  currency: 'usd',
});
javascript

BigInt(整數但任意大)

const big = 9007199254740993n;          // 結尾 n
big + 1n                                 // 9007199254740994n
Number.MAX_SAFE_INTEGER                  // 9007199254740991

// 不能跟 Number 直接運算
1n + 1                                   // ❌ TypeError
1n + BigInt(1)                           // 2n ✅
javascript

BigInt 解的是「超過 Number 安全範圍的大整數」(> 2^53),不是浮點精度。但配合「整數位元儲存」可以處理超大金額(虛擬貨幣 / 衛星測量等)。


各情境決策

情境用什麼
顯示 UI 金額(已從 server 取得)toFixed(2)
計算購物車總額Decimal.js
金融 / 計費整數位元 + Decimal.js
累加大量百分比Decimal.js
Number 比較Number.EPSILON
超大整數(如使用者 ID、訂單編號)BigInt

其他語言也有同問題

不是 JS 獨有,任何 IEEE 754 的語言都會踩:

# Python
0.1 + 0.2 == 0.3            # False
print(0.1 + 0.2)            # 0.30000000000000004
python
// Java
double a = 0.1 + 0.2;
System.out.println(a == 0.3);   // false

// 解法:BigDecimal
import java.math.BigDecimal;
BigDecimal x = new BigDecimal("0.1").add(new BigDecimal("0.2"));
// x = 0.3 精確
java

Java 的 BigDecimal、Python 的 Decimal、JS 的 Decimal.js — 都是同一思路。


實際 React 範例(縮放比例)

// ❌ 浮點累加,N 次後誤差爆炸
const [scale, setScale] = useState(1);
const handleZoom = () => setScale(prev => prev + 0.1);

// ✅ 用 Decimal.js
import Decimal from 'decimal.js';
const handleZoom = () => {
  setScale(prev => new Decimal(prev).plus(0.1).toNumber());
};

// ✅ 或先放大 100 倍當整數運算
const [scaleX100, setScaleX100] = useState(100);
const handleZoom = () => setScaleX100(prev => prev + 10);
const displayScale = scaleX100 / 100;
typescript

何時可以裝沒看到

不必每次都用 Decimal
如果你的 誤差容忍度大於 1e-10(顯示 2 位小數的 UI、CSS animation、Game scaling),直接用 Number 就好。 只在以下場景 必須處理:
  1. 金錢、稅務、計費
  2. 累加 / 累乘 100+ 次(誤差會累積)
  3. 跟其他系統(API、DB)交換精確數字

目錄

    ◆ 相關文章

    • Python Self learning

      2026-05-09
    • Java SE 6 技術手冊

      2026-05-09
    • Class-Based & Prototype-Based

      Class-Based & Prototype-Based

      2026-05-09
    • tsx 轉 jsx

      2026-05-09
    ← 上一篇cookie & sessionStorage加密 (crypto-js)下一篇 →html link作法

    ◆ 關於作者

    Kurau

    個人寫作 / 創作的 SoT,記錄遊戲、影視、學習與生活。

    更多 Kurau 的文章