使用BigDecimal运算出现Non-terminating decimal expansion; no exact representable decimal result.

我们都知道Java提供了两个高精度计算的类:BigInteger和BigDeciaml。虽然它们大体上属于“包装器类”的范畴,但两者都没有对应的基本类型。BigDeciaml支持任意精度的定点数。例如,可以用它进行精确的货币计算。(以上摘自Java编程思想第4版)

最近的项目使用BigDeciaml,代码如下:

/**
* @param originPrice 原始货币金额
* @param betweenCurrencyRate 原始货币兑换中间货币的汇率
* @param toCurrencyRate 中间货币兑换目标货币的汇率
* @return java.math.BigDecimal
*/
public static BigDecimal rate(BigDecimal originPrice,BigDecimal betweenCurrencyRate,BigDecimal toCurrencyRate){

   return originPrice.multiply(betweenCurrencyRate).divide(toCurrencyRate);
}

BigDecimal chaoIn = exchangeRate.getChaoIn();
BigDecimal cNYChaoIn = exchangeRateCNY.getChaoIn();

adultPrice = RateExchangeUtil.rate(adultPrice,chaoIn,cNYChaoIn);
childPrice = RateExchangeUtil.rate(childRetail.multiply(new BigDecimal(childNum)),chaoIn,cNYChaoIn);
// 计算酒店里面总价
hotelTotalPrice = hotelTotalPrice.add(childPrice).add(adultPrice);

在使用BigDecimal做运算的时候,出现了下面的异常:

Non-terminating decimal expansion; no exact representable decimal result.

后来通过搜索得知,通过BigDecimal的divide方法进行除法时当不整除,出现无限循环小数时,就会抛出上面的异常。
我们应该使用divide的重载方法:

BigDecimal.divide(BigDecimal divisor, int scale, RoundingMode roundingMode) ;

scale为小数位数;

roundingMode为小数模式;
ROUND_CEILING
如果 BigDecimal 是正的,则做 ROUND_UP 操作;如果为负,则做 ROUND_DOWN 操作。
ROUND_DOWN
从不在舍弃(即截断)的小数之前增加数字。
ROUND_FLOOR
如果 BigDecimal 为正,则作 ROUND_UP ;如果为负,则作 ROUND_DOWN 。
ROUND_HALF_DOWN
若舍弃部分> .5,则作 ROUND_UP;否则,作 ROUND_DOWN 。
ROUND_HALF_EVEN
如果舍弃部分左边的数字为奇数,则作 ROUND_HALF_UP ;如果它为偶数,则作 ROUND_HALF_DOWN 。
ROUND_HALF_UP
若舍弃部分>=.5,则作 ROUND_UP ;否则,作 ROUND_DOWN 。
ROUND_UNNECESSARY
该“伪舍入模式”实际是指明所要求的操作必须是精确的,,因此不需要舍入操作。
ROUND_UP
总是在非 0 舍弃小数(即截断)之前增加数字。

最后修正rate方法中的问题,代码如下:

/**
*  货币转换 美元转加元,人民币作为中间货币
*  美元 * 美元兑换人民币的汇率 / 人民币兑换加元的汇率
*  出现问题 Non-terminating decimal expansion; no exact representable decimal result.
*  要指定 divide的后两个参数
*  JAVA中如果用BigDecimal做除法的时候一定要在divide方法中传递scale参数,
*  定义精确到小数点后几位,否则在不整除的情况下,结果是无限循环小数时,就会抛出以上异常。
*
* @param originPrice 原始货币金额
* @param betweenCurrencyRate 原始货币兑换中间货币的汇率
* @param toCurrencyRate 中间货币兑换目标货币的汇率
* @return java.math.BigDecimal
*/
public static BigDecimal rate(BigDecimal originPrice,BigDecimal betweenCurrencyRate,BigDecimal toCurrencyRate){

    return originPrice.multiply(betweenCurrencyRate).divide(toCurrencyRate,4,BigDecimal.ROUND_HALF_UP);
}

此处评论已关闭