加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

BigDecimal精度与相等比较的坑

发布时间:2020-12-14 03:46:17 所属栏目:大数据 来源:网络整理
导读:先想一下,创建BigDecimal对象的时候一般是怎么创建的? new一个,传进去值 BigDecimal.valueOf方法,传进去值 ? 作为一个数字类型,经常有的操作是比较大小,有一种情况是比较是否相等。用equal方法还是compareTo方法?这里就是一个大坑 1 // new 传进去一

先想一下,创建BigDecimal对象的时候一般是怎么创建的?

  • new一个,传进去值
  • BigDecimal.valueOf方法,传进去值

?

作为一个数字类型,经常有的操作是比较大小,有一种情况是比较是否相等。用equal方法还是compareTo方法?这里就是一个大坑

 1 //new 传进去一个double
 2 BigDecimal newZero = new BigDecimal(0.0);
 3 System.out.println(BigDecimal.ZERO.equals(newZero));
 4  
 5 //new 传进去一个字符串
 6 BigDecimal stringNewZero = new BigDecimal("0.0");
 7 System.out.println(BigDecimal.ZERO.equals(stringNewZero));
 8  
 9 //valueOf  传进去一个double
10 BigDecimal noScaleZero = BigDecimal.valueOf(0.0);
11 System.out.println(BigDecimal.ZERO.equals(noScaleZero));
12  
13 //valueOf  传进去一个double,再手动设置精度为1
14 BigDecimal scaleZero = BigDecimal.valueOf(0.0).setScale(1);
15 System.out.println(BigDecimal.ZERO.equals(scaleZero));

?

用于比较的值全都是0,猜一猜上面几个equals方法返回的结果是什么?全都是true?no no no...

true
false
false
false

?

惊不惊喜,意不意外?原因是什么呢?看一下BigDecimal的equals方法的实现:

 1 public boolean equals(Object x) {
 2     //类型不同,直接返回false
 3     if (!(x instanceof BigDecimal))
 4         return false;
 5     BigDecimal xDec = (BigDecimal) x;
 6     //同一个对象,直接返回true
 7     if (x == this)
 8         return true;
 9     //精度不同,直接返回false!!
10     if (scale != xDec.scale)
11         return false;
12     long s = this.intCompact;
13     long xs = xDec.intCompact;
14     if (s != INFLATED) {
15         if (xs == INFLATED)
16             xs = compactValFor(xDec.intVal);
17         return xs == s;
18     } else if (xs != INFLATED)
19         return xs == compactValFor(this.intVal);
20  
21     return this.inflated().equals(xDec.inflated());
22 }

?

从前面三个简单的判断就可以看出来,debug跟一下就知道是上面equals方法有三个返回false,都是因为精度不同。那么BigDecimal.ZERO的精度是多少呢?看下源码:

 1 // Cache of common small BigDecimal values.
 2 private static final BigDecimal zeroThroughTen[] = {
 3     new BigDecimal(BigInteger.ZERO,1), 4     new BigDecimal(BigInteger.ONE,1, 5     new BigDecimal(BigInteger.valueOf(2),2, 6     new BigDecimal(BigInteger.valueOf(3),3, 7     new BigDecimal(BigInteger.valueOf(4),4, 8     new BigDecimal(BigInteger.valueOf(5),5, 9     new BigDecimal(BigInteger.valueOf(6),6,10     new BigDecimal(BigInteger.valueOf(7),7,11     new BigDecimal(BigInteger.valueOf(8),8,12     new BigDecimal(BigInteger.valueOf(9),9,13     new BigDecimal(BigInteger.TEN,10,2),14 };
15  
16  
17 /**
18  * The value 0,with a scale of 0.
19  *
20  * @since  1.5
21  */
22 public static final BigDecimal ZERO = zeroThroughTen[0];

BigDecimal.ZERO值为0,精度为0.

?

而上面几种返回false的case,都是因为精度不同。精度不同的原因,则是BigDecimal对象初始化的方式不同,从源码上看,前三种初始化的方式都不同。

所以说,BigDecimal比较大小,还是用compareTo方法比较靠谱,改为compareTo之后,上面四个case返回的结果都是相等:

 1 BigDecimal newZero = new BigDecimal(0.0);
 2 System.out.println(BigDecimal.ZERO.compareTo(newZero));
 3  
 4 BigDecimal stringNewZero = new BigDecimal("0.0");
 5 System.out.println(BigDecimal.ZERO.compareTo(stringNewZero));
 6  
 7 BigDecimal noScaleZero = BigDecimal.valueOf(0.0);
 8 System.out.println(BigDecimal.ZERO.compareTo(noScaleZero));
 9  
10 BigDecimal scaleZero = BigDecimal.valueOf(0.0).setScale(1);
11 System.out.println(BigDecimal.ZERO.compareTo(scaleZero));

?

输出结果

0
0
0
0

?

由此联想到的一个更大的坑是,如果将BigDecimal的值作为HashMap的key,因为精度的问题,相同的值就可能出现hashCode值不同并且equals方法返回false,导致put和get就很可能会出现相同的值但是存取了不同的value。

再想一想,小数类型在计算机中本来就不能精确存储,再把其作为HashMap的key就相当不靠谱了,以后还是少用。

?

另外需要注意的一点是,写代码调别人写的方法时,最好是点进去看一下实现。再小再常用的方法,都可能埋着大坑

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读