码迷,mamicode.com
首页 > 其他好文 > 详细

多么痛的领悟---关于RMB数据类型导致的元转分分转元的bug

时间:2017-09-13 23:15:03      阅读:211      评论:0      收藏:0      [点我收藏+]

标签:系统   精度   遇到   存在   概率   数据   代码   设置   mon   

关于金额的数据类型,以及元转分分转元之间这种转换,以元和分的比较,我相信很多人都踩过坑。

反正我是踩过。

而且,昨天和今天又重重的踩了两脚。

 

代付查询接口,支付中心给溢+响应的报文里,amount的单位是分,这无可厚非,非常合理。

昨天,负责溢+代付的中威反映,有一单虽然返回的是代付成功的状态,但因校验支付中心返回的代付金额与溢+存储的代付金额不一致,而导致溢+未能更新代付单的状态。

经查,db里代付金额字段的数据类型是double,单位是元,程序里对应的pojo也把代付金额的属性设置为double。

出问题的那一单的代付金额是1049.11,支付中心响应的代付金额是104910。显然,这会致使溢+校验代付金额失败。

如下是赋值代码:
responseModel.setAmount((int) (record.getPayMoney() * 100)); //元转分

测试发现,Double的1049.11经这么转换后,果然是104910。拍拍脑袋,这自然是double的数据精度的问题了。又进一步测试了几个临近的数:1049.10→104909,1049.11→104910,1049.12→104911,并且1049.13可以正常转换为104913
于是,尝试将代付金额的数据类型改为float,经测试,改为float是可以的。
之前做结算系统时也遇到过类似问题。由于手头工作较多,这里不再继续了解double和float的区别了。

走申请,上线!

到了今天下午,溢+那边又反映,说又存在了3笔,支付中心返回了错误的代付金额。

/汗

其中一笔的代付金额是20.38,支付中心响应给溢+的值是2037。另外还有两笔,151.4→15139;32.85→3284

不能再那么敷衍了。

同事说之前项目用的都是BigDecimal。我将信将疑,写了个测试用例,来看看到底BigDecimal与Double/Float取值有哪些不同:

技术分享

技术分享


通过看测试数据,发现,无论BigDecimal/Double/Float,其intValue()方法,返回的值都是整数部分, 不会像Math.round()那样做进行四舍五入。因为我上面贴出来的那条元转分的语句,(int) (record.getPayMoney() * 100)等价于(record.getPayMoney() * 100).intValue(),所以,转换得到的分就会出现因浮点型数据精度而导致的少1分的小概率情况。

那天有同事问我为什么interface的方法不用public修饰,我从OO角度跟他解释了原因。 不琢磨,一些简单的问题也搞不清。而我今天,也同样遭遇了他的那种情况。


最后,因为支付中心是从.net翻版的,我打开visualstudio,发现,.net给代付金额定义的类型也是decimal。
于是,果断将代付金额的数据类型重构为BigDecimal。

 

多么痛的领悟---关于RMB数据类型导致的元转分分转元的bug

标签:系统   精度   遇到   存在   概率   数据   代码   设置   mon   

原文地址:http://www.cnblogs.com/buguge/p/7517871.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!