使用不当记录,不涉及原理性的分析。
使用BigDecimal进行小数乘除运算,出来的结果不及预期。
public static void test(){
BigDecimal oneHundred = new BigDecimal(100);
BigDecimal percent = BigDecimal.valueOf(15.00);
BigDecimal num1 = new BigDecimal(36.90).setScale(2, BigDecimal.ROUND_HALF_DOWN);
//num1:36.90
System.out.println("num1:"+num1);
BigDecimal c1 = new BigDecimal(0.01).setScale(2, BigDecimal.ROUND_HALF_DOWN);
//c1:0.01
System.out.println("c1:"+c1);
BigDecimal result1 = num1
.multiply(oneHundred.subtract(percent))
.multiply(c1);
//result1:31.36500
System.out.println("result1:"+result1);
result1=result1.setScale(2, BigDecimal.ROUND_HALF_DOWN);
//result1:31.36
System.out.println("result1:"+result1);
System.out.println("----------------------------- ");
BigDecimal num2 = new BigDecimal(36.90).setScale(2, BigDecimal.ROUND_HALF_DOWN);
//num2:36.90
System.out.println("num2:"+num2);
BigDecimal c2 = new BigDecimal(0.01);
//c2:0.01000000000000000020816681711721685132943093776702880859375
System.out.println("c2:"+c2);
BigDecimal result2 = num2
.multiply(oneHundred.subtract(percent));
//result2:3136.500
System.out.println("result2:"+result2);
result2=result2.multiply(c2);
//result2:31.36500000000000065291522188815065419476013630628585815429687500
System.out.println("result2:"+result2);
result2=result2.setScale(2, BigDecimal.ROUND_HALF_DOWN);
//result2:31.37
System.out.println("result2:"+result2);
}
输出结果都写在注释上。
两次的代码区别在于对待0.01上,一个进行了精度控制,一个没有。
教训:对于小数一定要进行精度控制。
这里再复习下BigDecimal中精度控制的常量含义:
ROUND_CEILING
接近正无穷大的舍入模式。如果 BigDecimal 为正,则舍入行为与 ROUND_UP 相同;如果为负,则舍入行为与 ROUND_DOWN 相同。注意,此舍入模式始终不会减少计算值。
ROUND_DOWN
接近零的舍入模式。在丢弃某部分之前始终不增加数字(即截短)。注意,此舍入模式始终不会增加计算值的大小。
ROUND_FLOOR
接近负无穷大的舍入模式。如果 BigDecimal 为正,则舍入行为与 ROUND_DOWN 相同;如果为负,则舍入行为与 ROUND_UP 相同。注意,此舍入模式始终不会增加计算值。
ROUND_HALF_DOWN
向“最接近的”数字舍入,如果与两个相邻数字的距离相等,则为上舍入的舍入模式。如果舍弃部分 > 0.5,则舍入行为与 ROUND_UP 相同;否则舍入行为与 ROUND_DOWN 相同。
36.36500000000000065-->36.37
36.3650-->36.36
BigDecimal num = new BigDecimal(36.365000).setScale(4, BigDecimal.ROUND_HALF_DOWN); System.out.println(num); System.out.println(num.setScale(2, BigDecimal.ROUND_HALF_DOWN));
ROUND_HALF_EVEN
向“最接近的”数字舍入,如果与两个相邻数字的距离相等,则向相邻的偶数舍入。如果舍弃部分左边的数字为奇数,则舍入行为与 ROUND_HALF_UP 相同;如果为偶数,则舍入行为与 ROUND_HALF_DOWN 相同。注意,在重复进行一系列计算时,此舍入模式可以将累加错误减到最小。
ROUND_HALF_UP
向“最接近的”数字舍入,如果与两个相邻数字的距离相等,则为向上舍入的舍入模式。如果舍弃部分 >= 0.5,则舍入行为与 ROUND_UP 相同;否则舍入行为与 ROUND_DOWN 相同。注意,这是我们大多数人在小学时就学过的舍入模式。
ROUND_UNNECESSARY
断言请求的操作具有精确的结果,因此不需要舍入。如果对获得精确结果的操作指定此舍入模式,则抛出 ArithmeticException。
ROUND_UP
舍入远离零的舍入模式。在丢弃非零部分之前始终增加数字。注意,此舍入模式始终不会减少计算值的大小。