zoukankan      html  css  js  c++  java
  • 多么痛的领悟---关于RMB数据类型导致的元转分分转元的bug

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

    反正我是踩过。

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

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

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

    经查支付中心,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。

  • 相关阅读:
    源码篇——安卓启动流程
    Android源代码结构分析
    使用Contacts Contract Content Provider操作通讯录最佳实践
    Linux输入子系统 : 按键驱动
    运算类实现 及 GNU Makefile基本结构
    输入系统:epoll & inotify
    FFmpeg:初步编译使用[Android]
    Linux学习 :SPI通讯协议
    Linux学习 : 自己写bootloader
    Linux学习 :移植U-boot_2012.04.01到JZ2440开发板
  • 原文地址:https://www.cnblogs.com/buguge/p/7517871.html
Copyright © 2011-2022 走看看