浮点数字段用=比较几乎必然失败,因IEEE 754二进制近似存储导致0.1、99.99等值存入即失真,实际存储如99.98999999999998,使SELECT 0.1+0.2=0.3返回0,WHERE amount=99.99查不到数据。

直接用 = 比较浮点数字段,几乎必然失败——不是语法错,是值本身就不可靠。
浮点数存进去那一刻就不是你写的那个数
0.1、99.99 这类十进制小数,在 IEEE 754 二进制下是无限循环小数,必须截断近似存储。MySQL 插入 99.99,实际存的是类似 99.99000000000002 或 99.98999999999998 的值。后续所有读取、计算、比较,都基于这个“假值”展开。
- 执行
SELECT 0.1 + 0.2 = 0.3返回0(即 false),因为左边算出的是0.30000000000000004 -
WHERE amount = 99.99查不到刚插入的那条记录,哪怕你确认 INSERT 语句写的就是99.99 - 不同数据库对字面量
99.99的默认类型推断不同:PostgreSQL 当作NUMERIC,MySQL 可能当DOUBLE,导致子查询和外层隐式转换路径不一致
嵌套查询里浮点匹配失效更隐蔽
子查询返回浮点值后,外层再用 = 去匹配,等于拿两个独立近似值做精确相等判断——误差叠加,几乎不可能为真。
- 错误写法:
SELECT * FROM users WHERE id IN (SELECT user_id FROM payments WHERE price = 99.99),常返回空结果 - 正确做法:在子查询内部就把浮点字段转成定点数,确保参与
IN、=、JOIN的值是精确可比的 - PostgreSQL:
price::DECIMAL(10,2) = 99.99 - MySQL:
CONVERT(price, DECIMAL(10,2)) = 99.99 - SQL Server:
CAST(price AS DECIMAL(10,2)) = 99.99
别指望 ROUND 或 ABS 补救,得从源头掐断
ROUND(price, 2) = 99.99 看似合理,但 ROUND() 返回类型不统一:MySQL 返回 DOUBLE,PostgreSQL 返回 NUMERIC,SQLite 可能返回 INTEGER——你根本不知道它又掉回浮点陷阱了。
-
SUM(ROUND(price, 2))是无效补救:聚合前误差已固化,ROUND 只是给错误结果套个马甲 - 真正有效的路径是:
SUM(CAST(price AS DECIMAL(10,2))),每行先转定点数,再累加 -
WHERE ABS(price - 99.99) < 0.001能查到数据,但无法走索引,性能差,且容差值需谨慎选(太小漏数据,太大误匹配) - 已有 FLOAT 字段,改类型只是第一步;存量数据里的近似值不会自动变精确,必须清洗
最危险的不是报错,而是静默失效:MySQL 不警告、不提示,只给你一个“看起来对”的错值。财务对账差三毛,根源往往就藏在这条看似无害的 WHERE price = 99.99 里。

















