背??景我们在计算或者显示金额的时候经常会用到BigDecimal,在涉及到金额的时候也是非常推荐的类型。BigDecimal本身也提供了很多构造方法。这些构造器方法使用不当,可能会造成不必要的麻烦,甚至造成金钱损失,造成意外的资金损失。事故接下来,让我们看一下收银机发生的事故。|问题描述收银机计算商品数量报错,导致订单无法支付。|事故等级P0|事故过程如下:13:44,接到报警,订单支付失败,支付可用率降至60%13:50,快速回滚线上代码,恢复正常14:20,审核代码,发布前验证14:58发现问题,修改问题代码并上线,恢复上线|故障原因BigDecimal金额计算精度丢失。原因分析首先,我们用一段代码来复现问题根源,如下:publicstaticvoidmain(String[]args){BigDecimalbigDecimal=newBigDecimal(88);System.out.println(bigDecimal);bigDecimal=newBigDecimal("8.8");System.out.println(bigDecimal);bigDecimal=newBigDecimal(8.8);System.out.println(bigDecimal);}执行结果如下:通过测试,当使用double或者float这些浮点数据类型时,会丢失精度,而String和int则不会。为什么?我们点开构造方法看源码:publicstaticlongdoubleToLongBits(doublevalue){longresult=doubleToRawLongBits(value);//根据位字段的值检查NaN,最大值//指数和非零有效和。如果(((结果&DoubleConsts.EXP_BIT_MASK)==DoubleConsts.EXP_BIT_MASK)&&(结果&DoubleConsts.SIGNIF_BIT_MASK)!=0L)结果=0x7ff8000000000000L;返回结果;int对应)提供了double和long的转换,doubleToRawLongBits就是把double转long,这个方法是原来的方法(底层不是java实现的,是c++实现的)。double之所以会出问题,是因为小数点转二进制会丢失精度。BigDecimal在处理时,将小数扩大N倍,使其可以在整数上进行计算,并保留相应的精度信息。①float和double类型主要是为科学计算和工程计算而设计的。执行二进制浮点运算的原因是为了对范围广泛的值提供更准确和快速的近似和计算。②不提供完全准确的结果,因此不宜用于精确结果。③当浮点数达到一定大数时,会自动使用科学计数法。这种表示只是实数的近似值,并不等于实数。④十进制数转二进制时会死循环或浮点数尾数超长。总结因此,在精度计算的过程中,我们尽量使用String类型进行转换。文章来源:https://c1n.cn/MSqAy
