浮点数无法精确表示十进制小数,导致0.1+0.2≠0.3;SUM累加会放大误差,ROUND仅表面修正无效;应先CAST为DECIMAL再运算,GROUP BY和WHERE中避免直接使用浮点比较。

浮点数在二进制中本就不能精确表示十进制小数
比如 0.1 和 0.2 在 IEEE 754 浮点标准下,是无限循环二进制小数,必须截断存储。所以 0.1 + 0.2 实际算出来是 0.30000000000000004,不是数学意义上的 0.3。SUM 对一列 FLOAT/DOUBLE 执行累加时,这个误差会逐轮累积、放大,最终体现在小数位后多出几位“噪声数字”。
SUM() 不修复浮点误差,只放大它
关键误区:以为 ROUND(SUM(price), 2) 能“修好”精度。其实不行——SUM(price) 已经用浮点算法算完了,误差已固化;ROUND 只是对一个错误结果做表面四舍五入,可能掩盖问题,但无法还原真实值。
-
SUM(CAST(price AS DECIMAL(15,2)))才是正解:每行先转成定点数,再全程用十进制加法累加 - 如果原始字段是
FLOAT,不 CAST 就直接 SUM,等于把所有误差打包求和 - MySQL 中两个
DECIMAL(10,2)相加,结果是DECIMAL(11,2);相乘则是DECIMAL(20,4)——类型推导有规则,别假设它“自动适配”
GROUP BY 或 WHERE 里用浮点计算会拆散本该合并的组
写 GROUP BY ROUND(price, 2) 看似按两位小数分组,实则危险:19.995 和 19.994999999999998 四舍五入后都是 20.00,但它们在浮点存储中是两个不同二进制值,ROUND 表达式每次计算结果可能因舍入方向微异,导致同一业务价格被分到不同组里。
- 正确做法是先
CAST(price AS DECIMAL(15,2)),再FLOOR(price * 100) / 100.0截断(非四舍五入) - WHERE 条件匹配浮点字段永远别用
=,比如WHERE price = 19.99几乎必不命中;应改用WHERE ABS(price - 19.99) 或先转 DECIMAL - SQL Server 还不支持在
GROUP BY里直接写表达式,得先在SELECT里定义别名,再引用
函数参数或变量声明漏精度,隐式转 DOUBLE 就掉坑里
哪怕你声明了 DECLARE total DECIMAL(15,2),只要调用时传的是 100 或 0.08 这种裸字面量,MySQL 就默认按 DOUBLE 解析参与运算,中间值一旦变成浮点,后续所有 CAST 都救不回来。
- 安全写法是传
100.0、0.0800,或显式CAST(100 AS DECIMAL(10,2)) - 存储过程里
DECLARE rate DECIMAL;是语法错误;DECLARE rate DECIMAL(10);也错——必须带标度,如DECIMAL(12,6) - 类型链断裂不会报错,只会静默漂移:你看到的结果“看起来对”,但和真实业务值差了几厘几毫,月底对账时才暴露
最麻烦的不是误差本身,而是它藏得深:不报错、不警告、不中断流程,只在 SUM 结果末尾多几个 0、在分组统计里少几条记录、在 WHERE 条件里悄悄跳过某一行——这些都得靠你提前把类型链拧紧,而不是等出事了再 round 补救。

















