ROUND函数作用对象是浮点近似值而非精确小数,如12.345在IEEE 754中可能存为12.344999…,导致ROUND返回12.34;须用DECIMAL类型先转换再ROUND,且财务场景必须采用ROUND(SUM())总额控制法避免累计误差。

ROUND 作用对象是浮点近似值,不是数学上的精确小数
你写的 ROUND(amount, 2) 看似在对“12.345”四舍五入,实际传给函数的很可能根本不是 12.345 —— 而是 IEEE 754 double 表示的近似值,比如 12.344999999999999。这个值小于 12.345,按规则向下取整得 12.34。这不是 ROUND 的错,是输入值从源头就失真了。
常见错误现象:
- 执行
SELECT ROUND(1.235, 2)得到1.23而非1.24 - 字段类型为
FLOAT或DOUBLE时,ROUND(col, 2)结果不稳定,同一条数据多次查询可能微变 - CSV 导入后直接
ROUND(@amount, 2),但@amount是字符串隐式转 float,误差已固化
不同数据库的 ROUND 默认舍入规则不统一
PostgreSQL 和 Oracle 默认用“银行家舍入”(四舍六入五成双),MySQL 和 SQL Server 多数版本倾向传统四舍五入。比如 ROUND(2.5, 0):
- PostgreSQL / Oracle →
2 - MySQL 5.7 / SQL Server →
3
这种差异在跨库对账、报表合并时会暴露:单笔差 0.01 元,百万级交易累计偏差可达数千元。更麻烦的是,某些数据库(如 SQL Server)的 ROUND 第三参数(控制舍入方向)仅 MySQL 8.0+ 支持,其他库无法对齐。
ROUND 不改变数据类型,显示位数由源类型决定
ROUND 只做数值计算,不负责格式化。如果输入是 FLOAT,输出仍是 FLOAT,客户端(如 SSMS、Navicat)会按 float 类型默认精度展示,出现 123.460000 这类末尾补零——这不是多出小数位,而是 float 类型的显示特性。
正确做法是让结果变成 DECIMAL:
- MySQL:
CAST(ROUND(col, 2) AS DECIMAL(15,2)) - PostgreSQL:
ROUND(col::NUMERIC, 2) - SQL Server:
CONVERT(DECIMAL(15,2), ROUND(col, 2))
注意:必须先 CAST 或 ::NUMERIC 把原始值转成精确类型,再 ROUND,顺序不能反。
SUM(ROUND()) 和 ROUND(SUM()) 语义完全不同
财务系统要求“总额控制”,即先加总、再四舍五入。但写成 SUM(ROUND(detail_amt, 2)) 是逐行舍入后相加,中间每一步都引入浮点误差,最终和 ROUND(SUM(detail_amt), 2) 可能差 0.01 元。
容易踩的坑:
- 明细表里用
ROUND(unit_price * qty, 2)存入临时表,再SUM()—— 误差已在明细层放大 - GROUP BY 后对每组
ROUND(SUM(), 2),但未确保 SUM 输入是 DECIMAL,底层仍是 float 累加 - 字段含
NULL或空字符串时,SUM自动忽略,但COALESCE(col, 0)若没配合CAST,仍可能触发隐式 float 转换
真正要控制精度的地方,从来不是最后一行 ROUND,而是建表时字段类型、ETL 入库时的 CAST、以及聚合前的类型显式声明。

















