前言
众所周知,double或float类型表示的小数有精度损失,所以一般在涉及到金钱相关时,推荐使用Bigdecimal类型。
但BigDecimal有如下几点需要特别注意。
1.推荐使用字符串构造BigDecimal对象
先看个反例:
public static void main(String[] args) {
System.out.println(new BigDecimal(1.2));
}
结果
下图为创建上述对象的构造方法
不要这样构造BigDecimal对象,会有精度损失。
正确代码如下
System.out.println(new BigDecimal("1.2"));
结果
使用字符串构造BigDecimal对象。符合预期结果,正确!
或者采用valueOf方法
System.out.println(BigDecimal.valueOf(1.2));
查看valueOf源码实现
本质上还是转成string。
但是,只能传入double类型的参数,如果传入的是float类型的字面量,同样存在精度问题。
System.out.println(BigDecimal.valueOf(1.2f));
总结:尽量使用字符串构造BigDecimal对象,当然如果传入的是double类型的,还可以使用valueOf方法。
2.对BigDecimal的操作不影响原对象
因为BigDecimal是不可变类,所以BigDecimal的相关运算都会返回一个新的对象,原本的对象值是不受改变的,这点和String类似。
以加法操作为例
BigDecimal a = new BigDecimal("1.1");
BigDecimal b = new BigDecimal("1.2");
BigDecimal c = a.add(b);
System.out.println(a);
System.out.println(c);
结果
总结
BigDecimal同String一样是不可变的,每次的四则运算都会返回一个新的对象,原本的对象是不变的。
3.小数取舍问题
BigDecimal a = new BigDecimal("1.255");
// 参数1:保留几位小数
// 参数2:保留规则,这里是四舍五入
BigDecimal b = a.setScale(2, ROUND_HALF_UP);
// 结果:1.26
System.out.println(b);
除法中的小数取舍问题
BigDecimal result = BigDecimal.valueOf(1.0).divide(BigDecimal.valueOf(0.3));
System.out.println(result);
结果
分析:正常的计算结果为3.33…,但BigDecimal无法表示,所以报错。
可以传入一个取舍规则,这里以保留两位小数,四舍五入为例。
BigDecimal result = BigDecimal.valueOf(1.0).divide(BigDecimal.valueOf(0.3), 2, ROUND_HALF_UP);
System.out.println(result);
4.不要用equals方法判断是否相等
BigDecimal a = new BigDecimal("1.00");
BigDecimal b = new BigDecimal("1.00");
BigDecimal c = new BigDecimal("1.0");
// true
System.out.println(a.equals(b));
// false:一般认为1.0 和 1.00 是相等的,但equals往往还会比较精度,所以不相等。
System.out.println(a.equals(c));
正确做法:使用compareTo方法比较两者的大小,然后再和0比较,等于0则相等
BigDecimal a = new BigDecimal("1.00");
BigDecimal b = new BigDecimal("1.00");
BigDecimal c = new BigDecimal("1.0");
// true
System.out.println(a.compareTo(b) == 0);
// true
System.out.println(a.compareTo(c) == 0);
总结
- 尽量使用字符串来构造BigDecimal对象
- BigDecimal是不可变的
- 间接使用compareTo判断是否相等
- 在除法操作时最好指定小数的保留规则