BigDecimal舍位模式与有效精度 -----商业与科学计算

发现身边不少同事在金融业务场景计算中使用近似计算,感到非常惊讶。
基于二进制有时无法精确地表示一个包含小数的数值,只能采用近似表示,这还不是最糟糕,更糟糕的是,我们在二进制表示的近似值上,利用计算机运算电路来计算得到的误差可能进一步放大,这种方式的唯一好处就是运算速度快,但可能无法得到精确的运算数值。
基于二进制有时无法精确地表示一个包含小数的数值,只能采用近似表示。因此,我们会发现0.3+0.3 !=0.6,对于有严格精确度要求的业务场景下是不能这么用。
为了实现精确计算,我们可以开发一个十进制运算的计算器程序,这样我们就能利用该程序进行运算处理,得到精确结果。这个计算器就是BigDecimal。

这里我们主要关注的是BigDecimal的舍位模式与有效数字位数设置

BigDecimal舍位模式:

ROUND_UP
进位舍入模式,在舍弃部分非零情况下,向前进位加1,始终增加数字(始终对非零舍弃部分前面的数字加1)。 
ROUND_DOWN
截断舍入模式,始终不进位。注意,此舍入模式始终不会增加计算值的大小。

ROUND_CEILING
舍入模式朝向正无穷大,如果BigDecimal是正的,则表现为ROUND_UP;如果为负,则表现为ROUND_DOWN。请注意,此舍入模式永远不会降低计算值。 

ROUND_FLOOR
舍入模式朝向负无穷大,如果BigDecimal是正的,则表现为ROUND_DOWN;如果为负,则表现为ROUND_UP。请注意,此舍入模式永远不会增加计算值。

ROUND_HALF_UP
向“最接近的”数字舍入,如果与两个相邻数字的距离相等,在这种情况下。如果舍弃部分大于等于0.5,则表现为ROUND_UP,否则,表现为ROUND_DOWN

ROUND_HALF_DOWN
向“最接近的”数字舍入,如果与两个相邻数字的距离相等,在这种情况下。如果舍弃部分大于0.5,则表现为ROUND_UP;否则,表现为ROUND_DOWN。 

ROUND_HALF_EVEN(银行家舍入法)
向“最接近的”数字舍入,如果与两个相邻数字的距离相等,在这种情况下,则向相邻的偶数舍入。即:
如果舍弃部分左边的数字为奇数,则舍入行为与 ROUND_HALF_UP相同;
如果舍弃部分左边的数字为偶数,则舍入行为与 ROUND_HALF_DOWN 相同。

注意,在重复进行一系列计算时,此舍入模式可以将累加错误减到最小。

ROUND_UNNECESSARY
断言请求的操作具有精确的结果,因此不需要舍入。
指定此舍入模式时,如果无法获得精确结果,则抛出ArithmeticException


MathContext 运算上下文(设置有效数字位数)
new MathContext(6, RoundingMode.UP)
6:表示最大有效数字位数为6位。(有效精度即:从左边第一个不是0的数字起,到末位数止),有效数字位数越多,精度越高,计算越准确(属于数学范畴,大家应该都能明白)
RoundingMode.UP:处理有效精度时可能需要用到的舍位模式

示例代码如下:

public class BigDecimalTest {

    public static void main(String[] args) {
        
        Double a = 0.66666663;
        Double b = 0.33333332;    
        Double c= a+b;
        System.out.println("a="+a.toString());
        System.out.println("b="+b.toString());
        System.out.println("c="+c.toString());
                        
        BigDecimal amount = new BigDecimal("123.5623");
        amount = amount.setScale(1, BigDecimal.ROUND_UP);
        System.out.println("BigDecimal.ROUND_UP: 123.5623 ="+amount.toString());
        
        amount = new BigDecimal("123.5523");
        amount = amount.setScale(1, BigDecimal.ROUND_UP);
        System.out.println("BigDecimal.ROUND_UP: 123.5523 ="+amount.toString());
        
        amount = new BigDecimal("123.5423");
        amount = amount.setScale(1, BigDecimal.ROUND_UP);
        System.out.println("BigDecimal.ROUND_UP: 123.5423 ="+amount.toString());
        
        amount = new BigDecimal("123.5623");
        amount = amount.setScale(1, BigDecimal.ROUND_DOWN);
        System.out.println("BigDecimal.ROUND_DOWN: 123.5623 ="+amount.toString());
        
        amount = new BigDecimal("123.5523");
        amount = amount.setScale(1, BigDecimal.ROUND_DOWN);
        System.out.println("BigDecimal.ROUND_DOWN: 123.5523 ="+amount.toString());
        
        amount = new BigDecimal("123.5423");
        amount = amount.setScale(1, BigDecimal.ROUND_DOWN);
        System.out.println("BigDecimal.ROUND_DOWN: 123.5423 ="+amount.toString());
        
        
        amount = new BigDecimal("123.5623");
        amount = amount.setScale(1, BigDecimal.ROUND_CEILING);
        System.out.println("BigDecimal.ROUND_CEILING: 123.5623 ="+amount.toString());
    
        amount = new BigDecimal("-123.5623");
        amount = amount.setScale(1, BigDecimal.ROUND_CEILING);
        System.out.println("BigDecimal.ROUND_CEILING: -123.5623 ="+amount.toString());
        
        amount = new BigDecimal("123.5423");
        amount = amount.setScale(1, BigDecimal.ROUND_CEILING);
        System.out.println("BigDecimal.ROUND_CEILING: 123.5423 ="+amount.toString());
        
        amount = new BigDecimal("-123.5423");
        amount = amount.setScale(1, BigDecimal.ROUND_CEILING);
        System.out.println("BigDecimal.ROUND_CEILING: -123.5423 ="+amount.toString());
        
        amount = new BigDecimal("123.5623");
        amount = amount.setScale(1, BigDecimal.ROUND_FLOOR);
        System.out.println("BigDecimal.ROUND_FLOOR: 123.5623 ="+amount.toString());
        
        amount = new BigDecimal("-123.5623");
        amount = amount.setScale(1, BigDecimal.ROUND_FLOOR);
        System.out.println("BigDecimal.ROUND_FLOOR: -123.5623 ="+amount.toString());
        
        amount = new BigDecimal("123.5423");
        amount = amount.setScale(1, BigDecimal.ROUND_FLOOR);
        System.out.println("BigDecimal.ROUND_FLOOR: 123.5423 ="+amount.toString());
        
        amount = new BigDecimal("-123.5423");
        amount = amount.setScale(1, BigDecimal.ROUND_FLOOR);
        System.out.println("BigDecimal.ROUND_FLOOR: -123.5423 ="+amount.toString());
        
        amount = new BigDecimal("123.5623");
        amount = amount.setScale(1, BigDecimal.ROUND_HALF_UP);
        System.out.println("BigDecimal.ROUND_HALF_UP: 123.5623 ="+amount.toString());
        
        amount = new BigDecimal("-123.5623");
        amount = amount.setScale(1, BigDecimal.ROUND_HALF_UP);
        System.out.println("BigDecimal.ROUND_HALF_UP: -123.5623 ="+amount.toString());
        
        amount = new BigDecimal("123.5423");
        amount = amount.setScale(1, BigDecimal.ROUND_HALF_UP);
        System.out.println("BigDecimal.ROUND_HALF_UP: 123.5423 ="+amount.toString());
        
        amount = new BigDecimal("-123.5423");
        amount = amount.setScale(1, BigDecimal.ROUND_HALF_UP);
        System.out.println("BigDecimal.ROUND_HALF_UP: -123.5423 ="+amount.toString());
        
        amount = new BigDecimal("123.5523");
        amount = amount.setScale(1, BigDecimal.ROUND_HALF_UP);
        System.out.println("BigDecimal.ROUND_HALF_UP: 123.5623 ="+amount.toString());
        
        amount = new BigDecimal("-123.5523");
        amount = amount.setScale(1, BigDecimal.ROUND_HALF_UP);
        System.out.println("BigDecimal.ROUND_HALF_UP: -123.5623 ="+amount.toString());
        
        amount = new BigDecimal("123.5623");
        amount = amount.setScale(1, BigDecimal.ROUND_HALF_DOWN);
        System.out.println("BigDecimal.ROUND_HALF_DOWN: 123.5623 ="+amount.toString());
        
        amount = new BigDecimal("-123.5623");
        amount = amount.setScale(1, BigDecimal.ROUND_HALF_DOWN);
        System.out.println("BigDecimal.ROUND_HALF_DOWN: -123.5623 ="+amount.toString());
        
        amount = new BigDecimal("123.5423");
        amount = amount.setScale(1, BigDecimal.ROUND_HALF_DOWN);
        System.out.println("BigDecimal.ROUND_HALF_DOWN: 123.5423 ="+amount.toString());
        
        amount = new BigDecimal("-123.5423");
        amount = amount.setScale(1, BigDecimal.ROUND_HALF_DOWN);
        System.out.println("BigDecimal.ROUND_HALF_DOWN: -123.5423 ="+amount.toString());
        
        amount = new BigDecimal("123.5523");
        amount = amount.setScale(1, BigDecimal.ROUND_HALF_DOWN);
        System.out.println("BigDecimal.ROUND_HALF_DOWN: 123.5623 ="+amount.toString());
        
        amount = new BigDecimal("-123.5523");
        amount = amount.setScale(1, BigDecimal.ROUND_HALF_DOWN);
        System.out.println("BigDecimal.ROUND_HALF_DOWN: -123.5623 ="+amount.toString());
        
        amount = new BigDecimal("123.5623");
        amount = amount.setScale(1, BigDecimal.ROUND_HALF_EVEN);
        System.out.println("BigDecimal.ROUND_HALF_EVEN: 123.5623 ="+amount.toString());
        
        amount = new BigDecimal("123.5423");
        amount = amount.setScale(1, BigDecimal.ROUND_HALF_EVEN);
        System.out.println("BigDecimal.ROUND_HALF_EVEN: 123.5423 ="+amount.toString());
        
        amount = new BigDecimal("123.2623");
        amount = amount.setScale(1, BigDecimal.ROUND_HALF_EVEN);
        System.out.println("BigDecimal.ROUND_HALF_EVEN: 123.2623 ="+amount.toString());
        
        amount = new BigDecimal("123.2423");
        amount = amount.setScale(1, BigDecimal.ROUND_HALF_EVEN);
        System.out.println("BigDecimal.ROUND_HALF_EVEN: 123.2423 ="+amount.toString());
        
        
        amount = new BigDecimal("123.5523");
        amount = amount.setScale(1, BigDecimal.ROUND_HALF_EVEN);
        System.out.println("BigDecimal.ROUND_HALF_EVEN: 123.5623 ="+amount.toString());
        
        amount = new BigDecimal("123.6523");
        amount = amount.setScale(1, BigDecimal.ROUND_HALF_EVEN);
        System.out.println("BigDecimal.ROUND_HALF_EVEN: 123.5423 ="+amount.toString());
        
        amount = new BigDecimal("123.5623");
        amount = amount.abs(new MathContext(8, RoundingMode.UP));//用于数值有效数字位数设置
        System.out.println("BigDecimal.ROUND_UP: 123.5623 ="+amount.toString());
        
        amount = amount.abs(new MathContext(6, RoundingMode.UP));//用于数值有效数字位数设置
        System.out.println("BigDecimal.ROUND_UP: 123.5623 ="+amount.toString());
        
        amount = amount.abs(new MathContext(4, RoundingMode.UP));//用于数值有效数字位数设置
        System.out.println("BigDecimal.ROUND_UP: 123.5623 ="+amount.toString());
        
        amount = amount.abs(new MathContext(2, RoundingMode.UP));//用于数值有效数字位数设置
        System.out.println("BigDecimal.ROUND_UP: 123.5623 ="+amount.toString());
        
    }
}
 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值