概述
发现身边不少同事在金融业务场景计算中使用近似计算,感到非常惊讶。
基于二进制有时无法精确地表示一个包含小数的数值,只能采用近似表示,这还不是最糟糕,更糟糕的是,我们在二进制表示的近似值上,利用计算机运算电路来计算得到的误差可能进一步放大,这种方式的唯一好处就是运算速度快,但可能无法得到精确的运算数值。
基于二进制有时无法精确地表示一个包含小数的数值,只能采用近似表示。因此,我们会发现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());
}
}
最后
以上就是飘逸钢笔为你收集整理的BigDecimal舍位模式与有效精度 -----商业与科学计算的全部内容,希望文章能够帮你解决BigDecimal舍位模式与有效精度 -----商业与科学计算所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复