被辞退了,因为小数点计算错误

发布时间:2023年12月28日

今年IT寒冬,大厂都裁员或者准备裁员,作为开猿节流主要目标之一,我们更应该时刻保持竞争力。为了抱团取暖,林老师开通了知识星球,并邀请我阿里、快手、腾讯等的朋友加入,分享八股文、项目经验、管理经验等,帮助大家提升技能,安稳度过这个寒冬,快扫描(长按)加入我们吧!

故事背景

今天我一个同事跟我吐槽,说他朋友因为程序问题,被公司辞退了,而且还没有任何补偿。我一听马上问,是删库跑路了嘛,这么严重。他说比这个还严重,说因为BigDecimal小数点四舍五入出现问题,导致订单金额偏低,公司损失了十几万美金,而且因为发现的晚,订单都已经发货了,钱要不回来了,造成很大的影响。虽然他朋友是公司老员工,但是发生这么大的事情,也只能引咎辞职了,而且因为个人问题导致公司权益受损,公司有权辞退,并且不进行任何赔偿。

故事带来的教训

我听完这个事情,久久无法回神,对众多小公司而言,因为用户量不高,服务宕机一段时间,其实不会直接造成非常大的影响(滴滴这种独角兽除外),而金额计算错误导致的问题,大多数都是非常致命的错误,目前金额计算一般都采用BigDecimal来进行运算,但是如果BigDecimal不会用或者没用好,也是会造成严重的线上问题。

为了引以为戒,博主特意整理了BigDecimal的易错场景,来培训团队成员,让大家引以为戒,可以用好BigDecimal,计算好金额,保住自己的饭碗。

易错点一:BigDecimal构造参数导致精度丢失问题

BigDecimal?decimal?=?new?BigDecimal(0.01);
打印实际值:0.01000000000000000020816681711721685132943093776702880859375
建议使用:BigDecimal.valueOf()方法赋值,比如:
BigDecimal?decimal1?= BigDecimal.valueOf(0.01);
同时,如果在高并发或者大量对象创建场景时,也不建议使用new BigDecimal方式创建对象,否则会影响性能,同样推荐BigDecimal.valueOf方式创建对象。

知识点:所以推荐使用BigDecimal.valueOf来赋值,确保金额数据的精度正确。

易错点二:正确使用两个BigDecimal对象的大小比较

num1.equals(num2) 或者 num1.compareTo(num2) 都是比较两个数的大小,但是它们有区别:
equals会先比较值,再比较精度;而compareTo直接比较值,不会比较精度。
BigDecimal?decimal2?=?new?BigDecimal("0.01");?BigDecimal?decimal3?=?new?BigDecimal("0.010");?//false,因为精度不一样 decimal2.equals(decimal3); //true, 只比较数值,不会比较精度 decimal2.compareTo(decimal3)==0;

知识点:所以要根据业务正确选择比较大小方法,确保业务逻辑的正确性。

易错点三:做除法运算时,必须设定精度和选择正确的舍入模式

BigDecimal?d1?= BigDecimal.valueOf(1.00);?BigDecimal?d2?= BigDecimal.valueOf(3.00);?BigDecimal?d3?= d1.divide(d2);

上述代码执行后,会直接抛出异常:java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
正确写法:
// 精度设置为2位,选择向远离零的方向四舍五入?BigDecimal?d3?= d1.divide(d2, 2, RoundingMode.HALF_UP);
BigDecimal舍入模式:

  • RoundingMode.UP:向远离零的方向舍入
  • RoundingMode.DOWN:向靠近零的方向舍入
  • RoundingMode.CEILING:向正无穷方向舍入
  • RoundingMode.FLOOR:向负无穷方向舍入
  • RoundingMode.HALF_UP:向远离零的方向四舍五入
  • RoundingMode.HALF_DOWN:向靠近零的方向四舍五入
  • RoundingMode.HALF_EVEN:银行家舍入法,遵循IEEE 754标准

知识点:所以做除法运算时,务必设定精确位数,避免系统异常崩溃。并且正确理解舍入模式的含义,有助于满足业务的需求。

易错点四:BigDecimal对象一旦设值不可修改原则问题

BigDecimal使用setScale方法设置精度时,原对象不会被修改,需要用新对象去接收。
BigDecimal?num?=?new?BigDecimal("1.2345");?//?num变量并没有变化,还是4位小数;?num.setScale(2, RoundingMode.HALF_UP);?//?result 才是四舍五入后并保持2位小数点。?BigDecimal?result?= num.setScale(2, RoundingMode.HALF_UP);

知识点:我们要理解不可变的特性,不要出现理解上的歧义,导致业务出现问题。

复盘反思

BigDecimal只是Java功能中小到不能再小的功能点,但是却有这么多的注意事项。写了这么多年代码之后,我可以明显的感觉到,初级开发和高级开发有着非常明显区别,特别是对待程序严谨性上,经验越多的程序员,可以想到更多的异常场景,从而保证最后的开发质量。

林老师带你学编程知识星球,创始人由工作10年以上的一线大厂人员组成,希望通过我们的分享,帮助大家少走弯路,可以在技术领域不断突破和发展。

具体的加入方式

星球内容涵盖:Java技术栈、Python、大数据、项目实战、面试指导等主题。

文章来源:https://blog.csdn.net/linzhiqiang0316/article/details/135257659
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。