BigDecimal避坑指南
在数据使用过程中会涉及到的精度丢失各种小坑情况。
1、通常在性能测试过程中我们对返回报文的数据精度关注度不是很高。当我们在返回报文看到精度丢失时,已经是一个很大的功能缺陷了。
2、是否可以在代码走读阶段就发现并避免这种涉及到精度计算的小坑呢?这需要一定的经验积累。
文章概要梳理
BigDecimal的4个坑及如何避免:
第一:浮点类型的坑
@Test
public void test1(){
BigDecimal a = new BigDecimal(0.01);
BigDecimal b = BigDecimal.valueOf(0.01);
System.out.println("a = " + a);
System.out.println("b = " + b);
}
a = 0.01000000000000000020816681711721685132943093776702880859375
b = 0.01
基本结论
1、,在使用BigDecimal构造函数时,尽量传递字符串而非浮点类型;
2,如果无法满足第一条,则可采用BigDecimal#valueOf方法来构造初始化值。
3,BigDecimal常见的构造方法BigDecimal(double) 创建一个具有参数所指定双精度值的对象。
double的构造方法,会出现上述的问题,使用时需特别留意。
第二:浮点精度的坑
@Test
public void test2(){
BigDecimal a = new BigDecimal(0.01);
BigDecimal b = new BigDecimal(0.010);
System.out.println(a.equals(b));//false
System.out.println(a.compareTo(b));//true
}
基本结论:通常情况,如果比较两个BigDecimal值的大小,采用其实现的compareTo方法;如果严格限制精度的比较,那么则可考虑使用equals方法。
另外,这种场景在比较0值的时候比较常见,比如比较BigDecimal("0")、BigDecimal("0.0")、BigDecimal("0.00"),此时一定要使用compareTo方法进行比较。
第三:设置精度的坑
@Test
public void test3(){
BigDecimal a = new BigDecimal("1.0");
BigDecimal b = new BigDecimal("3.0");
BigDecimal c1 = a.divide(b)
BigDecimal c2 = a.divide(b, 2,RoundingMode.HALF_UP);
System.out.println(c1 );//ArithmeticException异常!
System.out.println(c2);//0.33
}
基本结论:在使用BigDecimal进行(所有)运算时,一定要明确指定精度和舍入模式。
第四:三种字符串输出的坑
@Test
public void test4(){
BigDecimal a = BigDecimal.valueOf(35634535255456719.22345634534124578902);
System.out.println(a.toString());//3.563453525545672E+16
NumberFormat currency = NumberFormat.getCurrencyInstance(); //建立货币格式化引用
NumberFormat percent = NumberFormat.getPercentInstance(); //建立百分比格式化引用
percent.setMaximumFractionDigits(3); //百分比小数点最多3位
BigDecimal loanAmount = new BigDecimal("15000.48"); //金额
BigDecimal interestRate = new BigDecimal("0.008"); //利率
BigDecimal interest = loanAmount.multiply(interestRate); //相乘
System.out.println("金额:\t" + currency.format(loanAmount));//¥15,000.48
System.out.println("利率:\t" + percent.format(interestRate));//0.8%
System.out.println("利息:\t" + currency.format(interest));//¥120.00
}
基本结论:**根据数据结果展示格式不同,采用不同的字符串输出方法,通常使用比较多的方法为toPlainString()**。
根据需要选择输出模式
toPlainString():不使用任何科学计数法;
toString():在必要的时候使用科学计数法;
toEngineeringString() :在必要的时候使用工程计数法。类似于科学计数法,只不过指数的幂都是3的倍数,这样方便工程上的应用,因为在很多单位转换的时候都是10^3;