PostgreSQL中浮点值运算出现非预期高精度结果的技术问询
这其实是浮点数(尤其是PostgreSQL里默认的FLOAT/FLOAT(53),也就是IEEE 754标准的双精度浮点数)的经典二进制精度限制问题,可不是PostgreSQL的bug哦!
问题根源:浮点数的二进制存储限制
咱们先搞懂为什么会出现这种“奇怪”的结果:十进制里的很多小数(比如0.05、0.64)没法用二进制浮点数精确表示,它们在存储时已经是一个近似值了。举个例子:
CAST(669.05 AS FLOAT)实际存储的是一个接近669.05但略有偏差的二进制近似值CAST(1.64 AS FLOAT)同理,也是一个近似值
当这两个近似值相加时,误差被累积,就出现了670.6899999999999这种看起来不符合预期的结果。而你测试的1.63刚好运气好,两个近似值相加后的误差刚好被抵消到了能显示为670.68的程度,但这只是表象,底层的浮点值其实还是近似的。
另外你提到FLOAT(53)也有同样问题,这很正常——PostgreSQL里的FLOAT(53)就是双精度浮点数,和默认的FLOAT完全等价,本质都是IEEE 754标准的64位浮点数,所以精度限制是一样的。
解决方案:根据业务场景选择合适的处理方式
针对你的业务场景,有两种靠谱的处理思路:
1. 优先使用精确十进制类型(推荐精度敏感场景)
如果你的业务对精度要求很高(比如财务、金额计算),别再用FLOAT了,改用PostgreSQL的NUMERIC(或DECIMAL,两者完全等价)类型。它是基于十进制精确存储的,能完美避免这类精度问题。
比如把你的求和语句改成:
SELECT CAST(669.05 AS NUMERIC) + CAST(1.64 AS NUMERIC);
执行后就能得到精确的670.69。
对于全表运算的场景,直接把涉及计算的字段转换成NUMERIC再运算:
SELECT numeric_column_1 / CAST(numeric_column_2 AS NUMERIC) FROM input_table;
这样就能彻底解决大量精度异常的问题,不用再在应用层额外处理。
2. 用ROUND函数控制显示精度(适合性能优先场景)
如果你的业务必须用FLOAT(比如涉及大量科学计算,对性能要求更高),可以在查询结果里用ROUND函数指定精度,把近似值格式化成符合预期的小数位数:
SELECT ROUND(CAST(669.05 AS FLOAT) + CAST(1.64 AS FLOAT), 2);
这样输出结果就是670.69。不过要注意:ROUND只是改变了显示的格式,底层的浮点值还是近似的,只是输出时取了指定的精度。
总结
核心就是选对数据类型:
- 精度优先(比如金额、财务计算):用
NUMERIC - 性能优先且能接受近似值:用
FLOAT+ROUND控制显示精度
内容的提问来源于stack exchange,提问作者Shashank V C

