来源:/post/

Hi!我是山楂~

背景

我们在使用金额计算或者展示金额的时候经常会使用,也是涉及金额时非常推荐的一个类型。

自身也提供了很多构造器方法,这些构造器方法使用不当可能会造成不必要的麻烦甚至是金额损失,从而引起事故资损。

接下来我们看下收银台出的一起事故。

【问题描述】

收银台计算商品金额报错,导致订单无法支付。

【事故级别】

P0

【过程】

【故障原因】 在金额计算中丢失精度

原因分析

首先我们先用一段代码复现问题根源,如下所示:

public static void main(String[] args) {    BigDecimal bigDecimal=new BigDecimal(88);    System.out.println(bigDecimal);    bigDecimal=new BigDecimal("8.8");    System.out.println(bigDecimal);    bigDecimal=new BigDecimal(8.8);    System.out.println(bigDecimal);}

执行结果如下:

通过测试发现java保留两位小数,当使用或者float这些浮点数据类型时,会丢失精度java保留两位小数,、int则不会;这是为什么呢?

我们点开构造器方法看下源码:

public static long doubleToLongBits(double value) {    long result = doubleToRawLongBits(value);    // Check for NaN based on values of bit fields, maximum    // exponent and nonzero significand.    if ( ((result & DoubleConsts.EXP_BIT_MASK) ==          DoubleConsts.EXP_BIT_MASK) &&         (result & DoubleConsts.SIGNIF_BIT_MASK) != 0L)        result = 0x7ff8000000000000L;    return result;}

问题就处在 这个方法上,在jdk中类(float与int对应)中提供了与long转换,就是将转换为long,这个方法是原始方法(底层不是java实现,是c++实现的)。

之所以会出问题,是因为小数点转二进制丢失精度。在处理的时候把十进制小数扩大N倍让它在整数上进行计算,并保留相应的精度信息

float和类型,主要是为了科学计算和工程计算而设计的,之所以执行二进制浮点运算,是为了在广泛的数值范围上提供较为精确的快速近和计算。

并没有提供完全精确的结果,所以不应该被用于精确的结果的场合。

当浮点数达到一定大的数,就会自动使用科学计数法,这样的表示只是近似真实数而不等于真实数。

当十进制小数位转换二进制的时候也会出现无限循环或者超过浮点数尾数的长度。

总结

所以,在涉及到精度计算的过程中,我们尽量使用类型来进行转换,正确用法如下:

BigDecimal bigDecimal2=new BigDecimal("8.8");BigDecimal bigDecimal3=new BigDecimal("8.812");System.out.println( bigDecimal2.compareTo(bigDecimal3));System.out.println( bigDecimal2.add(bigDecimal3));

创建出来的是对象,我们不能用传统的加减乘除对其进行运算,必须使用他的方法,在我们数据库存储里,如果我们使用的是或者float类型,需要进行来回的转换后进行计算,非常不方便。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注