MySQL FLOAT (單精度) 與 DOUBLE (雙精度) 遵循 IEEE 754 標準,以二進位近似表示十進位實數,**天然存在精度誤差**。本文從二進位表示原理、MySQL/Oracle 類型對照、實測誤差案例、程式語言同源問題、金額欄位選型鐵律五個維度深度剖析,建立正確數值類型選型觀念。
FLOAT:~7 位有效數字 (2^24 ≈ 1.67e7)DOUBLE:~15-16 位有效數字 (2^53 ≈ 9.0e15)1310720000000000000000.66) 導致低位丟失。7.22、0.1、131072.32),捨入產生誤差。CREATE TABLE tt (num FLOAT(9,3));
INSERT INTO tt VALUES (123456.8); -- Out of range (超過 9 位總長度)
INSERT INTO tt VALUES (123456.8); -- 成功
SELECT * FROM tt; -- 123456.797 (近似值!)
INSERT INTO tt VALUES (123456.867); -- 123456.867 (精確,因在精度內)
INSERT INTO tt VALUES (2.8); -- 2.800
INSERT INTO tt VALUES (2.888888); -- 2.889 (四捨五入)
關鍵發現:
(M,D) 會報錯或截斷。123456.797 vs 123456.8)。WHERE num = 2.8 可匹配,但 WHERE num = 123456.8 **無法匹配** 存儲的 123456.797。| 分類 | MySQL | Oracle |
|---|---|---|
| IEEE 754 單精度 | FLOAT | BINARY_FLOAT |
| IEEE 754 雙精度 | DOUBLE / REAL | BINARY_DOUBLE |
| ANSI FLOAT(p) | FLOAT(p) (p≤24 單精度, p>24 雙精度) | FLOAT / FLOAT(n) |
| 定點數 (精確) | DECIMAL(p,s) / NUMERIC(p,s) | NUMBER(p,s) |
| 無精度指定浮點 | - | NUMBER (預設 38 位精度) |
**結論:** 只要遵循 IEEE 754,所有資料庫/語言的浮點數**都有相同精度問題**,非 MySQL 獨有。
// Java
System.out.println(7.22f - 7.0f); // 0.21999979 ≠ 0.22
// PHP
echo 7.22 - 7.0; // 0.22 (PHP 預設雙精度顯示截斷)
var_dump(7.22 - 7.0); // float(0.21999999999999997)
// Python
print(7.22 - 7.0) // 0.21999999999999997
// Go
fmt.Println(7.22 - 7.0) // 0.21999999999999997
鐵律: **絕不直接比較浮點數相等** (if (a == b)),改用誤差範圍 abs(a-b) < epsilon;**貨幣/精確計算嚴禁浮點數**。
| 場景 | 推薦類型 | 理由 |
|---|---|---|
| 貨幣金額 (元/分/厘) | DECIMAL(10,2) / BIGINT (存分) | 精確定點運算,無捨入誤差 |
| 科學測量、坐標、物理量 | DOUBLE | 15 位有效數字滿足多數科學計算 |
| 一般業務數值、比率、百分比 | DECIMAL(10,4) 或 DOUBLE | 視精度要求權衡 |
| 極大整數 (雪花ID、遞增ID) | BIGINT UNSIGNED | 64 位整數,無精度損失 |
-- 問題:WHERE float_col = 2.2 無法匹配存儲的 2.2000000476837158
-- 方案 1:改用 DOUBLE (較大精度,但仍有誤差)
-- 方案 2:指定格式化比較 (僅限小數位 ≤ 6)
SELECT * FROM t WHERE FORMAT(float_col, 3) = FORMAT(2.2, 3);
-- 方案 3:範圍查詢 (推薦)
SELECT * FROM t WHERE float_col BETWEEN 2.1999 AND 2.2001;
-- 方案 4:遷移至 DECIMAL (徹底解決)
ALTER TABLE t MODIFY float_col DECIMAL(10,3);
FLOAT 7 位有效數字,**超過 ±131072 (2^17) 精度急劇下降**;DOUBLE 15 位較安全但仍不精確。DECIMAL,不可留隱患。