先想一下,创建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...

  1. true
  2. false
  3. false
  4. 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, 0, 0, 1),
  4. new BigDecimal(BigInteger.ONE, 1, 0, 1),
  5. new BigDecimal(BigInteger.valueOf(2), 2, 0, 1),
  6. new BigDecimal(BigInteger.valueOf(3), 3, 0, 1),
  7. new BigDecimal(BigInteger.valueOf(4), 4, 0, 1),
  8. new BigDecimal(BigInteger.valueOf(5), 5, 0, 1),
  9. new BigDecimal(BigInteger.valueOf(6), 6, 0, 1),
  10. new BigDecimal(BigInteger.valueOf(7), 7, 0, 1),
  11. new BigDecimal(BigInteger.valueOf(8), 8, 0, 1),
  12. new BigDecimal(BigInteger.valueOf(9), 9, 0, 1),
  13. new BigDecimal(BigInteger.TEN, 10, 0, 2),
  14. };
  15.  
  16. /**
  17. * The value 0, with a scale of 0.
  18. *
  19. * @since 1.5
  20. */
  21. 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));

输出结果

  1. 0
  2. 0
  3. 0
  4. 0

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

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

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

BigDecimal精度与相等比较的坑的更多相关文章

  1. BigDecimal精度详解

    [BigDecimal精确度的计数保留法及精度丢失的解决办法] 目录 BigDecimal精确度的计数保留法 1.ROUND_UP 2.ROUND_DOWN 3.ROUND_HALF_UP 4.ROU ...

  2. BigDecimal进行除法运算时的坑

      循环小数输出的坑 BigDecimal做除法时如果出现除不尽(循环小数)的情况,会抛异常: BigDecimal a = new BigDecimal("1"); System ...

  3. Double与BigDecimal 精度问题

    转自:http://superivan.iteye.com/blog/963628 [1] 精确的浮点运算: 在Java里面,有时候为了保证数值的准确性需要精确的数据,先提供一个例子就可以发现问题了: ...

  4. BigDecimal的setScale()方法无效(坑)

    最近在使用BigDecimal进行四舍五入时,发现setScale()方法设置的精度值并没有起作用,一度让我怀疑起是否jdk有bug,代码如下: 错误代码 double d = 7.199999999 ...

  5. 关于json 转换BigDecimal精度丢失问题

    今天在转换一个关于金额字段发现一个关于json转换的bug  目前尚未深入观察 问题: 如果金钱为bigdecimal json转换后不会丢失精度 但是通过@responsebody 返回到前端后发现 ...

  6. BigDecimal精度问题

    介绍 1.商业计算使用BigDecimal. 2.使用参数为String的构造函数. 3.BigDecimal都是不可变的,每一步的运算时,都会产生一个新的对象.所以在做加减乘除后千万要保存操作后的值 ...

  7. Java中的BigDecimal类精度问题

    bigdecimal 能保证精度的原理是:BigDecimal的解决方案就是,不使用二进制,而是使用十进制(BigInteger)+小数点位置(scale)来表示小数,就是把所有的小数变成整数,记录小 ...

  8. BigDecimalAvoidDoubleConstructorRule:不要直接用double变量作为构造BigDecimal的参数

    先上结论: 不要直接用double变量作为构造BigDecimal的参数. 线上有这么一段Java代码逻辑: 1,接口传来一个JSON串,里面有个数字:57.3. 2,解析JSON并把这个数字保存在一 ...

  9. BigDecimal精确计算及陷阱

    BigDecimal通常在涉及到精确计算的时候会用到,下面是自己多次错误使用BigDecimal的总结. 结论: BigDecimal初始化小数时,尽量用字符串形式,例如new BigDecimal( ...

随机推荐

  1. sharepoint权限操作(记录以备忘)

    using Microsoft.SharePoint; using System; using System.Collections.Generic; using System.Linq; using ...

  2. /etc/issue

    /etc/issue 与 /etc/motd 作用一致,都是用于显示欢迎信息,区别在于 /etc/issue 是在 login 提示符之前显示,而 /etc/motd 则在在用户成功登录系统之后显示 ...

  3. open-falcon之HBS

    功能 处理agent心跳请求,并将agent信息(ip.hostname.agent_version.plugin_version)等信息入库(portal库) 为agent提供执行run api的白 ...

  4. 【十大算法实现之KNN】KNN算法实例(含测试数据和源码)

    KNN算法基本的思路是比较好理解的,今天根据它的特点写了一个实例,我会把所有的数据和代码都写在下面供大家参考,不足之处,请指正.谢谢! update:工程代码全部在本页面中,测试数据已丢失,建议去UC ...

  5. Qt自定义控件大全(一)云台仪表盘控件

    做过安防视频监控的同学都清楚,在视频监控系统软件上都可以看到一个云台控制区域,可以对球机进行下下左右等八个方位的运动控制,还可以进行复位,一般都是美工作图好,然后贴图的形式加入到软件中,好处是程序简单 ...

  6. Android 验证APK是否已经签名或是否是Debug签名

    https://source.android.google.cn/ http://www.android-doc.com/tools/publishing/app-signing.html Signi ...

  7. C陷阱与缺陷读书笔记

    2.1理解函数声明 这一章仔细分析了(*(void(*)())0)();这条语句的含义,并且提到了typedef的一种函数指针类型定义的用法. 我们经常用到的typedef用法是用于指定结构体的类型, ...

  8. 题目1004:Median(qsort函数自定义cmp函数)

    题目链接:http://ac.jobdu.com/problem.php?pid=1004 详解链接:https://github.com/zpfbuaa/JobduInCPlusPlus 参考代码: ...

  9. Protocol Buffers java

    Protocol Buffers https://developers.google.cn/protocol-buffers/ 一. 例 addressbook.proto. syntax = &qu ...

  10. 删除个别主机的Know_hosts文件信息

    方法一: rm -rf ~/.ssh/known_hosts 缺点:把其他正确的公钥信息也删除,下次链接要全部重新经过认证 方法二: vi ~/.ssh/known_hosts 删除对应ip的相关rs ...