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.
我们有以下代码:
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.