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。

  • 相关阅读:
    021.day21 反射 Class类 反射常用操作
    020.day20 线程概述 多线程优缺点 线程的创建 线程常用方法 生命周期 多线程同步
    019.day19 缓冲流 对象流 标准输入输出流
    018.day18 map集合如何实现排序 File类 IO流 字节流 字符流 编码
    017.day17 Map接口 克隆 treeSet集合排重缺陷
    016.day16 HashSet TreeSet 比较器Comparable Comparator
    015.day15
    014.day14
    013.day13
    线程
  • 原文地址:https://www.cnblogs.com/buguge/p/7517871.html
Copyright © 2011-2022 走看看