Java 的 Bigdecimal.divide 和舍入

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/26452894/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 02:32:17  来源:igfitidea点击:

Java's Bigdecimal.divide and rounding

javabigdecimal

提问by Raphael do Vale

At work, we found a problem when trying to divide a large number by 1000. This number came from the database.

在工作中,我们在尝试将一个大数除以 1000 时发现了一个问题。这个数来自数据库。

Say I have this method:

说我有这个方法:

private static BigDecimal divideBy1000(BigDecimal dividendo) {
    if (dividendo == null) return null;

    return dividendo.divide(BigDecimal.valueOf(1000), RoundingMode.HALF_UP);
}

When I make the following call

当我拨打以下电话时

divideBy1000(new BigDecimal("176100000"))

I receive the expected value of 176100. But if I try the line below

我收到了 176100 的预期值。但是如果我尝试下面的行

divideBy1000(new BigDecimal("1761e+5"))

I receive the value 200000. Why this occurs? Both numbers are the same with different representation and the latest is what I receive from database. I understand that, somehow, the JVM is dividing the number 1761 by 1000, rounding up and filling with 0's at the end.

我收到值 200000。为什么会出现这种情况?两个数字相同但表示不同,最新的是我从数据库中收到的数字。我知道,不知何故,JVM 将数字 1761 除以 1000,四舍五入并在末尾填充 0。

What is the best way to avoid this kind of behavior? Keep in mind that the original number is not controlled by me.

避免这种行为的最佳方法是什么?请记住,原始号码不受我控制。

采纳答案by dcernahoschi

As specified in javadoc, a BigDecimalis defined by an integer value and a scale.

如 javadoc 中所指定, aBigDecimal由一个整数值和一个scale定义。

The value of the number represented by the BigDecimal is therefore (unscaledValue × 10^(-scale)).

因此,BigDecimal 表示的数字的值是 (unscaledValue × 10^(-scale))。

So BigDecimal("1761e+5")has scale -5 and BigDecimal(176100000)has scale 0.

所以BigDecimal("1761e+5")有 -5 级和BigDecimal(176100000)0 级。

The division of the two BigDecimalis done using the -5 and 0 scales respectively because the scales are not specified when dividing. The dividedocumentationexplains why the results are different.

两者的除法BigDecimal是分别使用-5和0尺度来完成的,因为除法时没有指定尺度。该divide文档解释了为什么结果是不同的。

divide

public BigDecimal divide(BigDecimal divisor)

Returns a BigDecimalwhose value is (this / divisor), and whose preferred scale is (this.scale() - divisor.scale()); if the exact quotient cannot be represented (because it has a non-terminating decimal expansion) an ArithmeticExceptionis thrown.

Parameters:

divisor- value by which this BigDecimal is to be divided.

Returns:

this / divisor

Throws:

ArithmeticException— if the exact quotient does not have a terminating decimal expansion

Since:

1.5

divide

public BigDecimal divide(BigDecimal divisor)

返回一个 ,BigDecimal其值为(this / divisor),首选比例为(this.scale() - divisor.scale());如果无法表示确切的商(因为它具有非终止的十进制扩展),ArithmeticException则抛出一个。

参数:

divisor- 要除以该 BigDecimal 的值。

返回:

this / divisor

抛出:

ArithmeticException— 如果精确商没有终止的十进制扩展

自从:

1.5

If you specify a scale when dividing, e.g. dividendo.divide(BigDecimal.valueOf(1000), 0, RoundingMode.HALF_UP)you will get the same result.

如果在除法时指定比例,例如,dividendo.divide(BigDecimal.valueOf(1000), 0, RoundingMode.HALF_UP)您将获得相同的结果。

回答by Kevin Avignon

Yeah, that's kind of issue what you're experimenting. If I may, in a situation where you only have exponental numbers, you should cast them and then use your method. See what I suggest is this bit of code down there:

是的,这就是你正在试验的问题。如果可以,在您只有指数数的情况下,您应该转换它们然后使用您的方法。看看我的建议是下面的这段代码:

long  longValue = Double.valueOf("1761e+5").longValue();
BigDecimal value= new BigDecimal(longValue);

Use it in a method which would convert those string into a new BigDecimal and return this BigDecimal value. Then you can use those returned values with divideBy1000.That should clear any issue you're having.

在将这些字符串转换为新的 BigDecimal 并返回此 BigDecimal 值的方法中使用它。然后您可以将这些返回值与divideBy1000 一起使用。这应该可以解决您遇到的任何问题。

If you have a lot of those, what you can do also in store those BigDecimal in a data structure like a list. Then use a foreach loop in which you apply divideBy1000 and each new value would be stored in a different list. Then you would just have to access this list to have your new set of values !

如果您有很多这样的东西,您还可以将这些 BigDecimal 存储在列表等数据结构中。然后使用 foreach 循环,在其中应用divideBy1000,并且每个新值都将存储在不同的列表中。然后,您只需访问此列表即可获得新的值集!

Hope it helps :)

希望能帮助到你 :)

回答by Joshua

The expressions new BigDecimal("176100000")and new BigDecimal("1761e+5")are not equal. BigDecimalkeeps track of both value, and precision.

表达new BigDecimal("176100000")new BigDecimal("1761e+5")不相等的BigDecimal跟踪价值和精度。

BigDecimal("176100000")has 9 digits of precision and is represented internally as the BigInteger("176100000"), multiplied by 1. BigDecimal("1761e+5")has 4 digits of precision and is represented internally as the BigInteger("1761"), multiplied by 100000.

BigDecimal("176100000")有 9 位精度,内部表示为BigInteger("176100000"), 乘以 1。BigDecimal("1761e+5")精度为 4 位,内部表示为BigInteger("1761"),乘以 100000。

When you a divide a BigDecimalby a value, the result respects the digits of precision, resulting in different outputs for seemingly equal values.

当您将 aBigDecimal除以一个值时,结果尊重精度数字,从而导致看似相等的值的不同输出。

回答by Tetramputechture

Try using round().

尝试使用round().

private static BigDecimal divideBy1000(BigDecimal dividendo) {
    if (dividendo == null) return null;

    return dividendo.divide(BigDecimal.valueOf(1000)).round(new MathContext(4, RoundingMode.HALF_UP));
}

 public static void main(String []args){
    BigDecimal bigD = new BigDecimal("1761e5");
    BigDecimal bigDr = divideBy1000(bigD);
    System.out.println(bigDr);
 }

The new MathContext(4, RoundingMode.HALF_UP))line returns the division to 4 places.

new MathContext(4, RoundingMode.HALF_UP))行将除法返回到 4 个位置。

This produces:

这产生:

1.761E+5

Which is what you want. (:

这就是你想要的。(:

回答by meticoeus

Any time you are multiplying a BigDecimal by a power of 10, in this case you are multiplying by 10-3, you can use dividendo.scaleByPowerOfTen(power)which only modifies the scale of the BigDecimal object and side steps any rounding issues, or at least moves them to a later calculation.

任何时候您将 Bi​​gDecimal 乘以 10 的幂,在这种情况下,您乘以 10 -3,您都可以使用dividendo.scaleByPowerOfTen(power)它只修改 BigDecimal 对象的比例并回避任何舍入问题,或者至少将它们移动到以后计算。

The other answers here cover the more general case of dividing by any number.

这里的其他答案涵盖了除以任何数字的更一般情况。

回答by leandro lion

for your division with BigDecimal.

使用 BigDecimal 为您的除法。

dividendo.divide(divisor,2,RoundingMode.CEILING)//00.00 nothing for up and nothing for down

in this operation have a precision for two decimals.

在这个操作中有两位小数的精度。