首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫

MySQL 浮點數比較陷阱:DOUBLE/FLOAT 不精確本質與 DECIMAL 解決方案

     編輯:MySQL 資料庫相關知識

核心問題:二進制浮點數無法精確表示十進制小數

IEEE 754 雙精度 (DOUBLE) 與單精度 (FLOAT) 以二進制科學記號儲存,許多十進制小數(如 0.1、0.7)在二進制中為無限循環,導致近似值儲存。

實測案例:看似相等卻不等

CREATE TABLE t1 (i INT, d1 DOUBLE, d2 DOUBLE);
INSERT INTO t1 VALUES
(1, 101.40, 21.40), (1, -80.00, 0.00),
(2, 0.00, 0.00), (2, -13.20, 0.00), (2, 59.60, 46.40),
(2, 30.40, 30.40), (3, 37.00, 7.40), (3, -29.60, 0.00),
(4, 60.00, 15.40), (4, -10.60, 0.00), (4, -34.00, 0.00),
(5, 33.00, 0.00), (5, -25.80, 0.00), (5, 0.00, 7.20),
(6, 0.00, 0.00), (6, -51.40, 0.00);

SELECT i, SUM(d1) AS a, SUM(d2) AS b
FROM t1 GROUP BY i HAVING a <> b;

結果分析

+------+-------+------+
| i    | a     | b    |
+------+-------+------+
| 1    | 21.4  | 21.4 |
| 2    | 76.8  | 76.8 |
| 3    | 7.4   | 7.4  |
| 4    | 15.4  | 15.4 |
| 5    | 7.2   | 7.2  |
| 6    | -51.4 | 0    |
+------+-------+------+

前 5 組 a、b 顯示相同,但內部二進制表示不同,HAVING a <> b 判定為真。第 6 組明顯不同。

DECIMAL:精確儲存的解決方案

  • DECIMAL(M,D) 以字串形式儲存每一位數字,精度固定、運算不失真。
  • 將 d1、d2 改為 DECIMAL(10,2) 後,同樣查詢僅回傳第 6 行(真正不相等)。

最佳實踐指南

場景建議類型理由
金額、價格、費率、匯率DECIMAL(19,4) 或 DECIMAL(10,2)精確到分/厘,避免會計差異
科學計算、座標、測量數值DOUBLE需要極大範圍、容許微小誤差
圖形渲染、機器學習特徵FLOAT節省空間、GPU 友善
整數計數、ID、狀態碼INT/BIGINT精確、效能最佳

比較技巧:避免直接 = 比較

-- ❌ 錯誤
WHERE price = 19.99

-- ✅ 正確:容差比較
WHERE ABS(price - 19.99) < 0.0001

-- ✅ 正確:DECIMAL 欄位直接比較
WHERE price = CAST(19.99 AS DECIMAL(10,2))

MySQL 8.0 新特性:ROUND() 行為統一

MySQL 8.0 採用「銀行家捨入」 (Round half to even),ROUND(1.5)=2、ROUND(2.5)=2,減少累積偏差;舊版本為四捨五入。遷移時需注意。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved