java数字精度的坑----重点BigDecimal

double/float的数学计算会因为计算机的存储结构造成精度丢失,有时候会造成很严重的问题,这是众所周知的。。。一般这个时候会建议使用BigDecimal来规避这个问题,但是BigDecimal有个大坑要引起注意:

System.out.println(new BigDecimal(0.1).add(new BigDecimal(0.2)));
System.out.println(new BigDecimal(1.0).subtract(new BigDecimal(0.8)));
System.out.println(new BigDecimal(4.015).multiply(new BigDecimal(100)));
System.out.println(new BigDecimal(123.3).divide(new BigDecimal(100)));

以上的输出结果为:

0.3000000000000000166533453693773481063544750213623046875
0.1999999999999999555910790149937383830547332763671875
401.49999999999996802557689079549163579940795898437500
1.232999999999999971578290569595992565155029296875

似乎没有得到想要的值。要避开这个大坑,就必须准守:使用 BigDecimal 表示和计算浮点数,且务必使用字符串的构造方法来初始化 BigDecimal

System.out.println(new BigDecimal("0.1").add(new BigDecimal("0.2")));
System.out.println(new BigDecimal("1.0").subtract(new BigDecimal("0.8")));
System.out.println(new BigDecimal("4.015").multiply(new BigDecimal("100")));
System.out.println(new BigDecimal("123.3").divide(new BigDecimal("100")));

另外BigDecimal还有一个小坑1,那就是精度的问题

System.out.println(new BigDecimal("4.015").multiply(new BigDecimal(Double.toString(100))));

以上代码的输出并不是401.5,而是401.500。原因就是,BigDecimal 有 scale 和 precision 的概念,scale 表示小数点右边的位数,而 precision 表示精度,也就是有效数字的长度。对于 BigDecimal 乘法操作,返回值的 scale 是两个数的 scale 相加。
省脑筋的解决方案为,将输出的数值重新格式化一遍。

BigDecimal的小坑2,相等比较
不能用equal,而应该用compareTo

BigDecimal的小坑3,equals 和 hashCode 方法会同时考虑 value 和 scale,如果结合 HashSet 或 HashMap 使用的话就可能会出现麻烦
解决这个问题的办法有两个:

第一个方法是:使用 TreeSet 替换 HashSet。TreeSet 不使用 hashCode 方法,也不使用 equals 比较元素,而是使用 compareTo 方法,所以不会有问题。
第二个方法是:把 BigDecimal 存入 HashSet 或 HashMap 前,先使用 stripTrailingZeros 方法去掉尾部的零,比较的时候也去掉尾部的 0,确保 value 相同的 BigDecimal,scale 也是一致的。

你可能感兴趣的:(java基础)