BigDecimal : HALF_UP 使用 setScale 舍入

BigDecimal : HALF_UP rounding with setScale

我们有以下代码:

BigDecimal net = price
.divide(taxCumulative, RoundingMode.HALF_UP)
.setScale(2, BigDecimal.ROUND_UP); 

我们正在对此进行单元测试,并根据我们是否在测试中使用 @Transactional 类 获得不同的结果。

我只是想知道我们是否应该期望 HALF_UP 的申请与 setScale 一起或在它之前考虑。

例如:

说:

价格=4.00

taxCumulative = 1.20

您希望计算如下:

a) 4.00 / 1.20 = 3.33333333... --> HALF_UP --> 3 --> setScale --> 3

b) 4.00 / 1.20 = 3.33333333... --> HALF_UP with setScale 2 --> 3.33

就像我说的,我们对这段代码进行了单元测试,当我们有或没有 @Transactional 时,它的行为会有所不同。所以我们无法下结论。在现实世界中,结果是 b。不过a也有道理。

有什么想法吗?

更新

按照@Mark 的建议,我在spring 上下文之外创建了一个测试。 (但我想没有办法像codepen那样在线分享)。

package com.company;

import java.math.BigDecimal;
import java.math.RoundingMode;

public class Main {

    public static void main(String[] args) {
        BigDecimal price = new BigDecimal("4.00");
        BigDecimal taxCumulative = new BigDecimal("1.20");
        BigDecimal net = price.divide(taxCumulative, RoundingMode.HALF_UP).setScale(2, BigDecimal.ROUND_UP);
        System.out.println("With String constructor, the net =  " + net.toString());
        // prints 3.33
        price = new BigDecimal(4.00);
        taxCumulative = new BigDecimal(1.20);
        net = price.divide(taxCumulative, RoundingMode.HALF_UP).setScale(2, BigDecimal.ROUND_UP);
        System.out.println("With doubles, the net =  " + net.toString());
        // prints 3.00
    }
}

因此,正如@gtgaxiola 所指出的,String 构造函数有所不同。

以及关于使用setScale进行除法运算的问题。我又做了一些测试:

price = new BigDecimal("4.000000");
taxCumulative = new BigDecimal("1.2000000");
net = price.divide(taxCumulative, RoundingMode.HALF_UP).setScale(2, BigDecimal.ROUND_UP);
// 3.34

price = new BigDecimal("4.000000");
taxCumulative = new BigDecimal("1.2000000");
net = price.divide(taxCumulative, RoundingMode.HALF_UP);
// 3.333333

price = new BigDecimal("4");
taxCumulative = new BigDecimal("1.2");
net = price.divide(taxCumulative, RoundingMode.HALF_UP);
// 3

因此,结果在很大程度上取决于字符串输入的精度,并且应用 setScale 除法产生结果后。

它似乎会受到影响,具体取决于您构建 BigDecimal

的方式

查看 public BigDecimal(double val)

上的构造说明

1.- The results of this constructor can be somewhat unpredictable. One might assume that writing new BigDecimal(0.1) in Java creates a BigDecimal which is exactly equal to 0.1 (an unscaled value of 1, with a scale of 1), but it is actually equal to 0.1000000000000000055511151231257827021181583404541015625. This is because 0.1 cannot be represented exactly as a double (or, for that matter, as a binary fraction of any finite length). Thus, the value that is being passed in to the constructor is not exactly equal to 0.1, appearances notwithstanding.

2.-The String constructor, on the other hand, is perfectly predictable: writing new BigDecimal("0.1") creates a BigDecimal which is exactly equal to 0.1, as one would expect. Therefore, it is generally recommended that the String constructor be used in preference to this one.

3.-When a double must be used as a source for a BigDecimal, note that this constructor provides an exact conversion; it does not give the same result as converting the double to a String using the Double.toString(double) method and then using the BigDecimal(String) constructor. To get that result, use the static valueOf(double) method.