美文网首页青春校园互联网科技java高级开发群
程序员是如何神不知鬼不觉的弄丢银行1分钱的?

程序员是如何神不知鬼不觉的弄丢银行1分钱的?

作者: Java入门到入坟 | 来源:发表于2019-01-30 15:18 被阅读2次

前段时间和某银行合作共同开发了适合我们的一套支付系统。最近,我们对账发现某些订单始终都对不齐。银行的下单金额与对账金额始终少了1分钱。

这就奇怪了,如果这种异常订单一多就是少了很多钱。在涉及钱的金融领域,这是个很谨慎严肃的问题。

我们跟银行排查发现了问题的原因,也就是我今天想聊的关于技术上的东西:double精度的丢失问题

1问题复现

我们先举个简单的例子

doubleresult =1.0-0.9;

这段代码中result等于多少?0.1么?如果执行代码的话,分分钟打脸。

double精度丢失问题

2背后原理

无论是我们本文提到的double,还是float,都是浮点数。

在计算机科学中,浮点(英语:floating point,缩写为FP)是一种对于实数的近似值数值表现法,由一个有效数字(即尾数)加上幂数来表示,通常是乘以某个基数的整数次指数得到。以这种表示法表示的数值,称为浮点数(floating-point number)。

计算机使用浮点数运算的主因,在于计算机使用二进位制的运算。

例如:4÷2=2,4=100(二进制)、2=010(二进制)。在二进制中除以2相当于退一位数。

那么1.0÷2=0.5=0.1(二进制)也就是1/2,依此类推二进制的0.01(二进制)就是十进制 1/(2^2) = 1/4 = 0.25。

上面看到的1、0.5、0.25那都是可以转换成二进制的小数,如十进制的0.1,就无法用二进制准确的表示出来。因此只能使用近似值的方式表达。

比如,我们尝试着把10进制的0.1转化成二进制试试,步骤如下:

可以发现,这个过程是除不尽的,除出了一个无限循环小数:二进制的 0.0001100110011…

那么,如何在计算机中表示这个无限不循环的小数呢?只能考虑按照不同的精度保理不同的位数。

我们知道float是单精度的,double是双精度的。不同的精度,其实就是保留的有效数字位数不同,保留的位数越多,精度越高。

所以,浮点数在Java中是无法精确表示的,因为大部分浮点数转换成二进制是一个无限不循环的小数,只能通过保留精度的方式进行近似表示。

在《阿里巴巴Java开发手册》中其实也有着明确的规定,说明了小数类型禁止使用float或者double来表示。(虽然这条是Mysql相关规则,但是Java代码同样适用。)

3问题引申

我们现在基本已经知道了double的精度问题是什么问题。

在实际的订单交易过程中,出现这个问题的更多场景是金额单位元与分的转换。银行给你的单位是元,你自己的运算是分;前端输入是元,计算是分等等。

举个例子:用户下了一笔64.6元的订单,你在需要转换成分。如果直接除以100,你会发现计算出来的分始终是6459,少1分钱。

金额丢失1分问题

4解决方式

1.使用BigDecimal

为了解决这种浮点小数进度丢失问题,java提供了一种计算方式BigDecimal。

BigDecimal

这样就可以了么? 不是,这样能解决大部分问题,假如其他系统或语言不支持BigDecimal呢。当我们无法解决这个问题的时候,我们需要做的是想办法规避这个问题带来的影响。

2.以分为单位,Long为数据结构存储

目前我们某些核心系统在金额传输的过程和存储中还是以元存储浮点数。导致低于10元的订单计算利息费率的时候,无法计算清楚,使得我们的业务服务在处理这些问题头疼死了。

整数与整数的计算,就没有这些精度丢失问题。Long取值范围(9223372036854775807)完全够用。

3.除不尽怎么办

对于除法,始终会产生除不尽的情况怎么办?有个词叫轧差

什么意思呢?举个简单例子。假如现在需要把10元分成3分,如果是10除以3这么除,会发现为3.33333无穷尽的3。这些数字完全无法在程序或数据库中进行精确的存储。

这里我们的具体应用场景是用户使用了现金券,然后有部分退款,计算应退本金的问题。现金券的处理又是一大篇文章,这里以后有机会再介绍。

5总结

    1.  能用Long不用浮点数存储。

    2.  前后端传输金额(元)的时候,请使用字符串,不要使用浮点数。

    3.  浮点数运算请使用BigDecimal。

    4.  实在无法除尽,可以考虑通过轧差的方式解决。

double精度不是坑,是个容易忽视的巨坑,小小经验希望对大家有所帮助,谨慎对待。>-<

关于银行1分差的问题,等待银行修复,历史订单做调账处理,哈哈哈哈哈。

相关文章

  • 程序员是如何神不知鬼不觉的弄丢银行1分钱的?

    前段时间和某银行合作共同开发了适合我们的一套支付系统。最近,我们对账发现某些订单始终都对不齐。银行的下单金额与对账...

  • 程序员应该如何积累财富?

    程序员应该如何积累财富? 程序员应该如何积累财富? 2018余额不足 还有56天过年 六位数密码保护的银行卡余额还...

  • 2018-06-21

    银行是可以破产的,你的钱放在哪里安全? 原来很多人认为把大部分钱放在银行,他们相信银行是不会倒闭破产的,不过现在,...

  • 如何激发程序员潜力?

    本人是10余年的老程序员,对如何激发程序员潜力有点感悟。 程序员是比较理性的一个群体,0 1 是严格区分的,如何激...

  • 粮食存入粮食银行就是好

    粮食存入粮食银行给农民带来的好处: 1、增加农民收入:每年每斤粮食有6分钱的利息,高于一般商业银行存储利息。 2、...

  • 《分钱,花钱,赚钱》

    分钱,如何分钱?为什么分钱?分钱后可以获得什么? 花钱,如何花钱?为什么花钱?花钱后可以获得什么? 是否分完钱,花...

  • 实体店如何不花1分钱整合别人的鱼塘

    实体店如何不花1分钱,整合别人鱼塘的精准客流吸引进店 分享营销案例,传播营销思维! 实体店如何不花1分钱整合别人的...

  • 复习《香帅的北大金融课》-银行部分

    国家调控经济的措施 1、调节存款准备金 银行必须留存一小部分钱出来应付储户提现的需求,这部分钱就叫做存款准备金,这...

  • 甩甩宝宝小程序增长拆解

    本文通过甩甩宝宝小程序的三个活动来分析它是如何增长的。 1、1分钱购物 活动标题:疯狂砍价,1分钱买好物 玩法规则...

  • 我为什么想要存钱

    去银行存钱的人,肚子是饱暖的;去银行取钱的人,心情是焦躁的。其实你应该学点社会学了。 见过太多一分钱难倒英雄汉案例...

网友评论

    本文标题:程序员是如何神不知鬼不觉的弄丢银行1分钱的?

    本文链接:https://www.haomeiwen.com/subject/lmkcsqtx.html