ROUND返回1.23而非1.24是因为输入1.235被IEEE 754双精度近似为1.234999...,银行家舍入向下取整;应先CAST为DECIMAL再ROUND以确保精度。

ROUND 返回 1.23 而不是 1.24?问题不在 ROUND,而在输入值本身
执行 SELECT ROUND(1.235, 2) 得到 1.23,不是函数算错了,而是你传给它的根本就不是精确的 1.235。MySQL(以及 PostgreSQL、MaxCompute 等)会把字面量 1.235 当作 DOUBLE 解析,而 IEEE 754 双精度无法精确表示它——实际存储的是 1.234999999999999875...,这个值小于 1.235,银行家舍入规则下自然向下取整。
字符串参与运算时 ROUND 行为突变:'1.235' ≠ 1.235
当字段类型是 VARCHAR 或你写 ROUND('1.235', 2),MySQL 会按「字符串转数值」路径处理,但此时舍入规则切换为「四舍六入五成双」(即银行家舍入),且转换过程不保证十进制精度。更危险的是:如果表达式中混入字符串(如 field_a + '1.235'),整个结果变成字符串类型,ROUND 会基于隐式转换后的近似值计算,偏差不可控。
- 避免直接对
VARCHAR字段用ROUND,先用CAST(col AS DECIMAL(18,6)) - 不要依赖
ROUND('1.235', 2)的结果,它和ROUND(1.235, 2)语义不同、实现路径不同、结果也可能不同 - 在金融/计费类逻辑中,所有中间计算都应保持
DECIMAL类型,而非靠最后一步ROUND补救
DECIMAL 转换不是“多此一举”,而是切断浮点误差链的关键操作
CAST('1.235' AS DECIMAL(10,3)) 或 CAST(1.235 AS DECIMAL(10,3)) 的作用,是让数值在进入任何算术运算前,就以精确的十进制形式存在。后续的 ROUND(..., 2) 才真正作用于「数学意义上等于 1.235」的值,而不是它的二进制近似体。
- 推荐写法:
ROUND(CAST('1.235' AS DECIMAL(18,6)), 2)—— 字符串字面量无失真,DECIMAL 精确承载,ROUND 按预期工作 - 若字段原为
FLOAT/DOUBLE,必须先CAST(col AS DECIMAL(18,6))再ROUND,否则误差已在源头固化 - 注意
DECIMAL(m,d)的m(总位数)要足够容纳原始值,例如CAST('12345.6789' AS DECIMAL(10,4))合法,但DECIMAL(5,2)会溢出报错
MySQL 8.0+ 的第三参数 ROUND(x, d, 1) 仅解决舍入规则,不解决精度失真
ROUND(1.235, 2, 1) 确实能强制传统四舍五入(进位),但它依然作用于那个失真的 DOUBLE 值。所以 ROUND(1.235, 2, 1) 还是可能返回 1.23——因为底层值压根不到 1.235。这个参数只改规则,不修数据。
- 第三参数
1仅在 MySQL 8.0+ 有效,PostgreSQL/SQL Server 不支持 - 它不能替代
CAST,二者解决的是不同层面的问题:一个是「怎么舍」,一个是「舍谁」 - 真正需要确定性结果的场景(如账单、报表),必须同时满足:输入是
DECIMAL+ 舍入规则显式指定(如需)
最易被忽略的一点:误差不是在 ROUND 那一刻产生的,而是在第一个 FLOAT 字段读取、第一个字符串隐式转换、第一个 SUM 累加时就已埋下。等你看到 ROUND 结果不对,往往已经晚了。

















