BigDecimal
引入
在java语言中,double和float用于二进制浮点型计算,无法得到精确的结果。而BigDecimal则用于精确的计算。不超过16位有效数字(最好是不超过13位)的科学和工程计算,可以使用double和float,但要求精确计数或者超过了16位有效数字(超过13位也建议如此)的商业运算则需要使用BigDecimal进行运行,比如金融行业。
BigDecimal的介绍
BigDecimal 由任意精度的整数非标度值和32 位的整数标度(scale)值 组成。如果为零或正数,则标度是小数点后的位数。如果为负数,则将该数的非标度值乘以 10 的负scale 次幂。因此,BigDecimal表示的数值是(unscaledValue × 10-scale)。
BigDecimal的构造方法
public BigDecimal(double val)
将 double 转换为 BigDecimal,后者是 double 的二进制浮点值准确的十进制表示形式。返回的 BigDecimal 的标度是使 (10scale × val) 为整数的最小值。
注意:
- 此构造方法的结果有一定的不可预知性。
BigDecimal bd = new BigDecimal(0.1);
System.out.println(bd);
结果:0.1000000000000000055511151231257827021181583404541015625
- 另一方面,String 构造方法是完全可预知的:写入 new BigDecimal(“0.1”) 将创建一个 BigDecimal,它正好 等于预期的 0.1。
BigDecimal bd = new BigDecimal("0.1525");
System.out.println(bd);
结果:0.152
- 当 double 必须用作 BigDecimal 的源时,请注意,此构造方法提供了一个准确转换;它不提供与以下操作相同的结果:先使用 Double.toString(double) 方法,然后使用 BigDecimal(String) 构造方法,将 double 转换为 String。要获取该结果,请使用 static valueOf(double) 方法。
double a = 0.1525;
BigDecimal bigDecimal = BigDecimal.valueOf(a);
System.out.println(bigDecimal);
结果:
0.1525
金融行业中
1) 优先使用String参数的构造方法;
2)如果需要通过double构造BigDecimal,有限使用valueOf(double val)方法;这样得到的BigDecimal和String参数的构造方法标度一致(即标度可控);
3)valueOf()的静态工厂方法优先于long、int等类型参数的构造方法,
4)如果想获取指定标度的初始化数值,比如2位小数的0,可以如下使用:
BigDecimal zero1 = new BigDecimal(“0.00”);
String的构造方法,会根据字符串的小数位来设定标度,其他的构造方法还需要额外调用setScale()来设定标度。
BigDecimal的标度、舍入方式
表示 | 含义 |
---|---|
CEILING | 向正无限大方向舍入的舍入模式。 |
DOWN | 向零方向舍入的舍入模式。 |
FLOOR | 向负无限大方向舍入的舍入模式。 |
HALF_DOWN | 向最接近数字方向舍入的舍入模式,如果与两个相邻数字的距离相等,则向下舍入。 |
HALF_EVEN | 向最接近数字方向舍入的舍入模式,如果与两个相邻数字的距离相等,则向相邻的偶数舍入。 |
HALF_UP | 向最接近数字方向舍入的舍入模式,如果与两个相邻数字的距离相等,则向上舍入。 |
UNNECESSARY | 用于断言请求的操作具有精确结果的舍入模式,因此不需要舍入。 |
UP | 远离零方向舍入的舍入模式。 |
BigDecimal的四则运算
public BigDecimal add(BigDecimal value); //加法
public BigDecimal subtract(BigDecimal value); //减法
public BigDecimal multiply(BigDecimal value); //乘法
public BigDecimal divide(BigDecimal value); //除法
double a = 0.1525;
String s = Double.toString(a);
BigDecimal bd1 = new BigDecimal(s);
BigDecimal bd2 = new BigDecimal("0.25888");
BigDecimal bd3 = bd1.add(bd2);
System.out.println(bd3);
double a = 0.125;
String s = Double.toString(a);
BigDecimal bd1 = new BigDecimal(s);
BigDecimal bd2 = new BigDecimal("0.3");
BigDecimal bd3 = bd1.divide(bd2);
System.out.println(bd3);
结果:ArithmeticException - 如果准确的商值没有无穷的十进制扩展
可以看出,无法精确表示商值时就会抛异常,而除法很大可能无法精确表示商值。所以,必须指定标度和舍入方式。以下几个重载方法可以供调用者指定标度和舍入方式。
public BigDecimal divide(BigDecimal divisor, int scale, RoundingMode roundingMode)
public BigDecimal divide(BigDecimal divisor, RoundingMode roundingMode)
public BigDecimal divide(BigDecimal divisor, MathContext mc)
double a = 0.125;
String s = Double.toString(a);
BigDecimal bd1 = new BigDecimal(s);
BigDecimal bd2 = new BigDecimal("0.3");
BigDecimal bd3 = bd1.divide(bd2,5,FLOOR);
System.out.println(bd3);
结果:0.41666
BigDecimal的不可变性
BigDecimal有一个类似String的特性,就是不可变性。调用setScale或者add等运算方法后,原对象的scale和数值都不会改变,所以需要将方法的返回值进行保存使用。
比如:a = a.setScale(2); a = a.add(b). 其中a,b为BigDecimal对象。
总结
1)金融行业的金额一定要使用BigDecimal来表示;
2)BigDecimal最好使用String的构造方法创建,如果要使用double等数值作为参数,也要使用valueOf来创建对象。
3)BigDecimal进行四则运算时,最好指定其标度和舍入方式,否则可能抛异常。
4)BigDecimal是不可变的;四则运算后要使用对象保存结果;
5)多阅读相关API文档,写测试代码来验证其相关方法的调动,才能更好的掌握。