浮点数求和出错是IEEE 754二进制表示固有缺陷所致,根本解法是全程使用DECIMAL类型存储与计算,避免任何环节引入float。

为什么 SUM() 浮点数会出错?
直接对 REAL、FLOAT 或 DOUBLE 类型字段用 SUM(),结果常出现末尾几位不可控的误差(比如 0.1 + 0.2 = 0.30000000000000004)。这不是 MySQL 或 PostgreSQL 的 bug,而是 IEEE 754 二进制浮点表示法的固有缺陷——很多十进制小数根本无法精确存储。
常见错误现象:SUM(price) 返回 99.99999999999999 而不是预期的 100.00;财务对账时差 1 分钱;导出数据后 Excel 显示异常。
用 DECIMAL 替换浮点类型才是根本解法
数据库中存金额、重量、评分等需要精确计算的数值,必须用 DECIMAL(M,D)(如 DECIMAL(10,2)),而不是 FLOAT。这是设计阶段就要定死的,后期改代价高且不能自动修复历史数据精度问题。
-
DECIMAL是定点数,按十进制存储,加减乘除结果可预测、可审计 - 即使原始数据是浮点,也应在 INSERT/UPDATE 时显式转成
DECIMAL:INSERT INTO orders (amount) VALUES (CAST(19.99 AS DECIMAL(10,2))) - 已有浮点列想补救?可以新增
DECIMAL列,用ROUND(col, 2)初始化,再逐步切流量
临时补救:聚合前强制转 DECIMAL 再求和
如果无法立刻改表结构,必须在查询层控制精度。关键不是四舍五入结果,而是在求和过程中就用定点运算:
SELECT SUM(CAST(price AS DECIMAL(15,2))) AS total FROM sales;
注意几点:
-
CAST(... AS DECIMAL)必须在SUM()内部,写成SUM(price)::DECIMAL(PostgreSQL)或SUM(CAST(price AS DECIMAL))(MySQL/SQL Server)才有效 - 别用
ROUND(SUM(price), 2)——它先用浮点累加,再四舍五入,误差已产生 -
DECIMAL的精度位数要够用,比如累计超亿元的交易,至少用DECIMAL(18,2)
应用层也要同步处理,别只靠 SQL
SQL 层做了 CAST,但应用拿到的是字符串或 BigDecimal(Java)、Decimal(Python)还是 float?很多 ORM 默认把 numeric 字段映射成 float,等于白干。
- Python 使用
psycopg2时,启用register_decimal();用decimal.Decimal接收结果 - Java 的 JDBC 需设
useBigDecimal=true参数,否则ResultSet.getBigDecimal()可能退化为 double - Node.js 的
pg模块默认返回字符串,需手动new Decimal(row.total),避免parseFloat()
精度丢失从来不是单点问题,它是从建表、写入、查询到展示的一整条链路里,任意一环用了 float 就可能崩掉。最省事的方式,就是从第一行 CREATE TABLE 开始,就拒绝 FLOAT。

















