Java中BigDecimal精度和相等比较的坑

 更新时间:2018年09月13日 14:51:38   作者:八行书  
BigDecimal是一种精确的数字类,一般用于高精度的开发领域中,例如银行。下面这篇文章主要给大家介绍了关于Java中BigDecimal精度和相等比较的坑的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考下

为什么要有BigDecimal ,他是干什么的

float和double类型的主要设计目标是为了科学计算和工程计算。他们执行二进制浮点运算,这是为了在广域数值范围上提供较为精确的快速近似计算而精心设计的。然而,它们没有提供完全精确的结果,所以不应该被用于要求精确结果的场合。但是,商业计算往往要求结果精确,这时候就要使用BigDecimal啦。

什么是BigDecimal

BigDecimal 由任意精度的整数非标度值 和32 位的整数标度 (scale) 组成。如果为零或正数,则标度是小数点后的位数。如果为负数,则将该数的非标度值乘以 10 的负scale 次幂。因此,BigDecimal表示的数值是(unscaledValue × 10-scale)。

本文将给大家详细介绍关于Java中BigDecimal精度和相等比较的坑,下面话不多说了,来一起看看详细的介绍吧

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

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

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

//new 传进去一个double
BigDecimal newZero = new BigDecimal(0.0);
System.out.println(BigDecimal.ZERO.equals(newZero));
 
//new 传进去一个字符串
BigDecimal stringNewZero = new BigDecimal("0.0");
System.out.println(BigDecimal.ZERO.equals(stringNewZero));
 
//valueOf 传进去一个double
BigDecimal noScaleZero = BigDecimal.valueOf(0.0);
System.out.println(BigDecimal.ZERO.equals(noScaleZero));
 
//valueOf 传进去一个double,再手动设置精度为1
BigDecimal scaleZero = BigDecimal.valueOf(0.0).setScale(1);
System.out.println(BigDecimal.ZERO.equals(scaleZero));

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

true
false
false
false

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

public boolean equals(Object x) {
 //类型不同,直接返回false
 if (!(x instanceof BigDecimal))
  return false;
 BigDecimal xDec = (BigDecimal) x;
 //同一个对象,直接返回true
 if (x == this)
  return true;
 //精度不同,直接返回false!!
 if (scale != xDec.scale)
  return false;
 long s = this.intCompact;
 long xs = xDec.intCompact;
 if (s != INFLATED) {
  if (xs == INFLATED)
   xs = compactValFor(xDec.intVal);
  return xs == s;
 } else if (xs != INFLATED)
  return xs == compactValFor(this.intVal);
 
 return this.inflated().equals(xDec.inflated());
}

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

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

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

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

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

BigDecimal newZero = new BigDecimal(0.0);
System.out.println(BigDecimal.ZERO.compareTo(newZero));
 
BigDecimal stringNewZero = new BigDecimal("0.0");
System.out.println(BigDecimal.ZERO.compareTo(stringNewZero));
 
BigDecimal noScaleZero = BigDecimal.valueOf(0.0);
System.out.println(BigDecimal.ZERO.compareTo(noScaleZero));
 
BigDecimal scaleZero = BigDecimal.valueOf(0.0).setScale(1);
System.out.println(BigDecimal.ZERO.compareTo(scaleZero));

输出结果

0
0
0
0

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

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

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

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对脚本之家的支持。

相关文章

  • Java实现飞机大战游戏 附完整源码

    Java实现飞机大战游戏 附完整源码

    这篇文章主要介绍了Java实现飞机大战游戏,本文给大家分享完整源代码和效果图展示,对java飞机大战游戏实现代码感兴趣的朋友一起看看吧
    2022-05-05
  • JAVA错误:'无效目标发行版 17'的解决方案

    JAVA错误:'无效目标发行版 17'的解决方案

    这篇文章主要给大家介绍了关于JAVA错误:'无效目标发行版 17'的解决方案,文中通过图文介绍的非常详细,对大家学习或使用java具有一的的参考学习价值,需要的朋友可以参考下
    2022-09-09
  • Hadoop运行时遇到java.io.FileNotFoundException错误的解决方法

    Hadoop运行时遇到java.io.FileNotFoundException错误的解决方法

    今天给大家带来的是关于Java的相关知识,文章围绕着Hadoop运行时遇到java.io.FileNotFoundException错误展开,文中有非常详细的解决方法,需要的朋友可以参考下
    2021-06-06
  • Spring Cache和EhCache实现缓存管理方式

    Spring Cache和EhCache实现缓存管理方式

    这篇文章主要介绍了Spring Cache和EhCache实现缓存管理方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-06-06
  • Spring中HandlerMethod类源码详细解析

    Spring中HandlerMethod类源码详细解析

    这篇文章主要介绍了Spring中HandlerMethod类源码详细解析,HandlerMethod类用于封装控制器方法信息,包含类信息、方法Method对象、参数、注解等信息,具体的接口请求是可以根据封装的信息调用具体的方法来执行业务逻辑,需要的朋友可以参考下
    2023-11-11
  • Javafx简单实现【我的电脑资源管理器】效果

    Javafx简单实现【我的电脑资源管理器】效果

    这篇文章主要介绍了Javafx简单实现【我的电脑资源管理器】效果,涉及Javafx操作系统文件模拟资源管理器的实现技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-09-09
  • Spring Boot的properties配置文件读取

    Spring Boot的properties配置文件读取

    这篇文章主要介绍了Spring Boot的properties配置文件读取,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-06-06
  • java网上图书商城(2)Category模块

    java网上图书商城(2)Category模块

    这篇文章主要介绍了java网上图书商城,Category模块,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-12-12
  • 关于报错IDEA Terminated with exit code 1的解决方法

    关于报错IDEA Terminated with exit code 

    如果在IDEA构建项目时遇到下面这样的报错IDEA Terminated with exit code 1,那必然是Maven的设置参数重置了,导致下载错误引起的,本文给大家分享两种解决方法,需要的朋友可以参考下
    2022-08-08
  • Spring Boot与Spring MVC Spring对比及核心概念

    Spring Boot与Spring MVC Spring对比及核心概念

    这篇文章主要为大家介绍了Spring Boot与Spring MVC Spring的对比以及你需要了解的核心概念,有需要的朋友可以借鉴参考下,希望能够有所帮助
    2022-03-03

最新评论