一、bigdecimal精度计算类介绍

bigdecimal是java api中提供的一个用于精度计算的一个类,很多情况下,我们的Java项目中在做购买支付或者是价格计算时由于JavaSE中提供的四类八种数据类型或多或少的都会有精度损失,在对金钱计算方面极不安全,因此绝大多数情况下会用到bigdecimal类做精度方面的计算,从而保证数据或者是数字的绝对精确与安全。

今天的主角就是divide方法,该方法就是bigdecimal类中的一个除法计算方法,由于该divide方法参数类型众多并且不易理解容易出现错误,因此本文着重讲解该方法中的参数具体的用法与注意事项。

二、bigdecimal.divide除法运算用法详解

1)语法结构

了解使用一个方法之前,最重要的就是查看其API的语法结构,下面我们来看一下divide方法的具体语法如下:

public BigDecimal divide(BigDecimal divisor,int scale, int roundingMode);

public BigDecimal divide(BigDecimal divisor, int roundingMode);

由上面的语法结构中,我们可以看出divide方法有两个重载方法,一个是两参的方法,另一个是三参的方法,它们之间唯一的区别就是多了一个参数,这个参数是很重要的,在后面下文中的注意事项中,我们会着重介绍该参数会带来哪些异常问题?

2)divide除法运算方法参数详解

首先我们来看这两个承载方法中的参数,第一个参数传入的是除数,比如说有两个数A除以B,第一个参数就相当于B,B就是除数。

第二个参数是一个整数类型,实际意思是最终结果小数点后面保留几位小数,而第三个参数就是小数点后面保留小数时省略或者进位的选择模式,该模式可以有多种选择。

前两个参数我们已经说得很清楚了,最令大家模糊不解的就是第三个参数,第三个参数有四种形式下面我们来看一下这四种形式的具体解读,bigdecimal.divide方法中 roundingMode的参数定义了四种模式,这四种模式分别对应的是bigdecimal类中定义的四个常量。

第一个名字叫BigDecimal.ROUND_DOWN,该模式的意思是,不管保留几位小数,也不管小数点后面的数字是多少,直接省略掉后面所有的小数,也就是说如果保留两位小数,那么第三位及以后的小数全部忽略掉,比如3.234,保留两位小数得到的结果就是3.23。

第二种参数模式叫BigDecimal.ROUND_UP,很明显up的意思就是上升一位,它的直接含义就是:如果我们也是保留两位小数的话,不管第三位小数的数字是几我们都直接前进进升一位,比如4.671保留两位小数得到的结果就是4.68。

第三种参数模式叫BigDecimal.ROUND_HALF_UP,Javaapi上的实际描述是四舍五入,意思就是四及以下下都舍掉,小数点后面的数字是五及以上都进一位,比如:最终的预算结果是3.45,如果只保留一位小数的话,那最后的结果就是3.5,但是该方法还有另一种变种模式就是下面的第四种模式。

divide方法中roundingMode参数的第四种模式叫BigDecimal.ROUND_HALF_DOWN,原意也是叫四舍五入的意思,但是它跟第三种模式有一个区别就是,如果保留小数时,小数位的下一位刚好是五的话,那这一个模式的结果就是会把这个5给舍掉,而不是进一位,比如同样是3.45的结果,如果保留一位小数的话,最终的结果就是3.4,这是这两个四舍五入的模式之间的唯一的区别,其它的都是相同的。

3)案例讲解ROUND_HALF_UP和ROUND_HALF_DOWN的注意事项

上文中讲到了bigdecimal.divide方法中roundingMode参数的四种可选择参数模型,其中第三第四种四舍五入的参数模式在实际使用时还是有些坑需要大家去注意,因此我们介绍下面的案例,来给大家梳理一下这两种参数,模式的坑到底在哪里,希望大家以后在应用的时候可以避免这些错误。

 

上图中,我们的案例代码中定义了两个bigdecimal类。其中d1与d2调用divide方法做除法运算,实际预算结果是45除以7等于6.42858。

但是后来我们发现,当我们在控制台打印计算结果时,当我们运用参数模型为BigDecimal.ROUND_HALF_UP和BigDecimal.ROUND_HALF_DOWN这两个四舍五入的参数模型时,控制台打印的结果是一样的,实际的结果都是6.429,这就是我们接下来为大家讲解了需要注意的坑。

为什么这两种计算方式得到的都是6.429呢?如果按照我们上文中的模式讲解应该一个是6.429,另一个是6.428才对啊,因为我们是保留三位小数,第四位小数是五,按照两种模式的计算方式一个应该是进一位,另一个应该舍掉才对啊,为什么会出现这种情况呢?

经过多方查阅资料,我们发现如果计算结果的总位数恰好比需要保留的位数多一位的话,并且多出来的一位刚好是5的话,它的计算结果是按照我们上文说up模式进一位、down模式舍掉一位,按照这种方式计算。

如果计算结果的总位数比实际的要保留的位数大于一位的话,它就默认会统一按照ROUND_HALF_UP的方式进行计算。

三、divide方法使用异常注意事项

下面我们讲解一下bigdecimal.divide方法使用时抛出异常的情况,有时候我们在项目中使用该方法进行计算时时而抛出异常时而不抛异常,异常信息为Non-terminating decimal expansion,具体异常截图如下:

 

从上面的分析中时而异常时而不异常的bug情况,我们分析应该不是语法的问题,应该是传入的数据有问题,后来发现,这个方法使用时,如果不传入第二个参数不设置保留几位小数的情况下,如果计算结果是无限循环的小数儿。就会抛出上文中的异常信息。

因此就出现了一会儿y一异常一会正常的情况,原来是计算的结果有无限循环的小数,因此,为了避免以后出现这种情况,有必要限制一下保留几位小数。