Java 如何解决四舍五入带来的不匹配问题?
我正在开发一个具有两个端点的JavaAPI。一个端点有待支付的总金额,另一个端点有端点一中总金额的细分或组成。背后的逻辑是从DB获取货币A的值,并以货币B向用户显示,汇率为1B=18.03A。理想情况下,转换后的总金额和转换后的细分金额之和必须相同。在大多数情况下,代码运行良好。但有时我会出现不匹配的情况。举个例子,, 从一号终点算起,总金额为19370.21亿美元。细分(从终点二开始):124.5B、4245.71B、15000B。下面是我进行转换时发生的情况Java 如何解决四舍五入带来的不匹配问题?,java,bigdecimal,rounding-error,Java,Bigdecimal,Rounding Error,我正在开发一个具有两个端点的JavaAPI。一个端点有待支付的总金额,另一个端点有端点一中总金额的细分或组成。背后的逻辑是从DB获取货币A的值,并以货币B向用户显示,汇率为1B=18.03A。理想情况下,转换后的总金额和转换后的细分金额之和必须相同。在大多数情况下,代码运行良好。但有时我会出现不匹配的情况。举个例子,, 从一号终点算起,总金额为19370.21亿美元。细分(从终点二开始):124.5B、4245.71B、15000B。下面是我进行转换时发生的情况 BigDecimal r
BigDecimal rate = BigDecimal .valueOf(18.03);
//Converting total amount to currency B
BigDecimal bgtotal = BigDecimal .valueOf(19370.21);
BigDecimal totalToCurB= bgtotal.divide(rate, 2, RoundingMode.HALF_UP);
System.out.println(totalToCurB);//prints 1074.33
//Converting individual item amounts to currency B and summing them
BigDecimal itemA = BigDecimal .valueOf(124.5);
BigDecimal itemB = BigDecimal .valueOf(4245.71);
BigDecimal itemC = BigDecimal .valueOf(15000);
BigDecimal itemAToCurB= itemA.divide(rate, 2, RoundingMode.HALF_UP);
BigDecimal itemBToCurB= itemB.divide(rate, 2, RoundingMode.HALF_UP);
BigDecimal itemCToCurB= itemC.divide(rate, 2, RoundingMode.HALF_UP);
BigDecimal total = itemAToCurB.add(itemBToCurB).add(itemCToCurB);
System.out.println(total);//prints 1074.34
如何避免这种不匹配?在操作上设置更大的比例。如果在算术运算中只使用两位小数,则会出现差异
itematorub
将为6.91、itemtocurb
235.48和itemtorub
831.95,这三者之和将是错误的。由于分区更像6.90515806988352745424,因此这里的问题是itematoruble
。通过在小数点后第二位四舍五入,您只是增加了差异
当你用更多的小数位进行除法时,舍入将在不太重要的尺度上进行。您总是可以只打印两个位置
差不多
BigDecimal rate = BigDecimal .valueOf(18.03);
BigDecimal itemA = BigDecimal .valueOf(124.5);
BigDecimal itemB = BigDecimal .valueOf(4245.71);
BigDecimal itemC = BigDecimal .valueOf(15000);
BigDecimal itemAToCurB= itemA.divide(rate, 20, RoundingMode.HALF_UP);
BigDecimal itemBToCurB= itemB.divide(rate, 20, RoundingMode.HALF_UP);
BigDecimal itemCToCurB= itemC.divide(rate, 20, RoundingMode.HALF_UP);
BigDecimal total = itemAToCurB.add(itemBToCurB).add(itemCToCurB);
System.out.println(total.setScale(2, RoundingMode.HALF_UP));//prints 1074.33
如所示,该问题是按比例引入的,将其增加到小数点后3位或4位将解决该问题
或者,您可以保留小数点后2位,但对于明细和总额,只能使用一个端点。也就是说,您可以调用端点2并对细分金额求和,而不是调用端点1来获取总金额
// Option 1: Increase decimal scale to 3 or 4
BigDecimal rate = BigDecimal .valueOf(18.03);
BigDecimal total = BigDecimal.valueOf(19370.21);
BigDecimal itemA = BigDecimal.valueOf(124.5);
BigDecimal itemB = BigDecimal.valueOf(4245.71);
BigDecimal itemC = BigDecimal.valueOf(15000);
BigDecimal itemAToCurB= itemA.divide(rate, 3, RoundingMode.HALF_UP); //6.905
BigDecimal itemBToCurB= itemB.divide(rate, 3, RoundingMode.HALF_UP); //235.48
BigDecimal itemCToCurB= itemC.divide(rate, 3, RoundingMode.HALF_UP); //831.947
BigDecimal totalA = itemAToCurB.add(itemBToCurB).add(itemCToCurB);
BigDecimal totalB = total.divide(rate, 2, RoundingMode.HALF_UP);
System.out.println(totalA.setScale(2, RoundingMode.HALF_UP)); //prints 1074.33
System.out.println(totalB.setScale(2, RoundingMode.HALF_UP)); //prints 1074.33
我还需要向客户显示细分金额。实际上,平等性检查是由客户端完成的。这里没有太多选项。若客户希望只有两位小数,那个么它总是会有舍入差异,这只是数学问题。你需要用更多的小数。然后只显示所需的数量增加小数位数无法解决问题。最后的办法是单独汇总细分数据。我想有一种方法可以使用BigDecimal属性来解决这个问题。无论如何,谢谢。
// Option 2: Use total from Endpoint 2, Carrying the rounding-off errors
BigDecimal rate = BigDecimal .valueOf(18.03);
// BigDecimal total = BigDecimal.valueOf(19370.21);
BigDecimal itemA = BigDecimal.valueOf(124.5);
BigDecimal itemB = BigDecimal.valueOf(4245.71);
BigDecimal itemC = BigDecimal.valueOf(15000);
BigDecimal itemAToCurB= itemA.divide(rate, 2, RoundingMode.HALF_UP); //6.91
BigDecimal itemBToCurB= itemB.divide(rate, 2, RoundingMode.HALF_UP); //235.48
BigDecimal itemCToCurB= itemC.divide(rate, 2, RoundingMode.HALF_UP); //831.95
BigDecimal totalA = itemAToCurB.add(itemBToCurB).add(itemCToCurB);
BigDecimal totalB = totalA;
System.out.println(totalA.setScale(2, RoundingMode.HALF_UP)); //prints 1074.34
System.out.println(totalB.setScale(2, RoundingMode.HALF_UP)); //prints 1074.34