C++和Delphi工程0.5四舍五入问题的的排查
Posted 李迟
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++和Delphi工程0.5四舍五入问题的的排查相关的知识,希望对你有一定的参考价值。
最近加班时,在测试数据过程发现1分钱的误差问题。经过排查,发现原来是数据库本身小数点存储的问题,但修改数据表类型已不可能,因此须改代码以适应之。本文记录了排查的过程。
发现问题
那天深夜,由外部人员核对数据时,发现某个字段数据与手工计算的结果少了1分钱,主任工十分关注,那天搞到深夜,经过多种语言在不同系统中测试发现的确如此,当时还不知道原因,因临近上线,最后决定手动修改错误的字段的数值,以确保按时上线。
排查过程
先是在用代码复现算法过程,将计算过程打印出来,均未发现异常,但最终结果依然出错。
后来发现,原来是数据表本身有误差。生产环境使用 oracle 数据库,该字段数值存储的是以元为单位的金额,本身只有2个小数点,但字段类型是 FLOAT,大小是 126。比如数值2.01
,存储时会变成2.0099999999999998
。如果与0.5
相乘,则变成1.0049999999999999
,转换成分,变成100.49999999999999
,此时四舍五入,结果为100
。如果按2.01
计算,中间值为1.005
,最终四舍五入的结果为101
。误差就是这样来的。在代码中用printf
打印,应该自动做了转换,即100.49999999999999
会以100.5
的形式输出,但只要做四舍五入操作,就会变成100
,可能在计算机系统中,100.49999999999999
与100.500000000
是不相等的。
这个问题是在 Delphi 工程中发现,用 C++ 语言在 Linux系统,Golang 语言在 Windows 系统测试,表现一样。
问题解决
试过将原始的数值先四舍五入再进行计算,无法解决。最后的方法是在计算结果中添加一个很小很小浮点数,比如0.00000001
,填补存在的误差,经核对,可以达到预期结果。但此法始终不是正道。
小结
个人认为最根本的解决方法是修改数据表的类型,或者修改数值表示形式,以减少小数点为原则,如单位为元,则转换为分;单位为公里,则转换为米,因为实际应用中,最小的单位就是分和米。但在生产环境已有的机制且对修改结果无法评估情况下,只能修改代码来解决。
以上是关于C++和Delphi工程0.5四舍五入问题的的排查的主要内容,如果未能解决你的问题,请参考以下文章