ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Java正确比较浮点数的方法

2022-08-30 11:01:38  阅读:247  来源: 互联网

标签:Java 正确 double 浮点数 sdiff d1 d2 BigDecimal


https://www.jb51.net/article/199220.htm

 

看下面这段代码,将 d1 和 d2 两个浮点数进行比较,输出的结果会是什么?

1 2 3 double d1 = .1 * 3; double d2 = .3; System.out.println(d1 == d2);

按照正常逻辑来看,d1经过计算之后的结果应该是0.3,最后打印的结果应该是 true,对吧?但是运行一下就会发现结果并不是 true 而是 false 

输出一下 d1,发现得到的答案不是想象中的 0.3 而是 0.30000000000000004,所以和 d2 进行比较结果自然是 false

如何正确地比较浮点数(单精度的 float 和双精度的 double),不单单是 Java 特定的问题,在计算机的内存中,存储浮点数时使用的是 IEEE 754 标准,就会有精度的问题。

存储和转换的过程中浮点数容易引起一些较小的舍入误差,正是这个原因,导致在比较浮点数的时候,不能使用“==”操作符——要求严格意义上的完全相等。

那么如何正确的比较浮点数呢?这里有两种方案。

  • 第一种方案是允许两个值之间存在一点误差(指定一个阈值),使用 Math.abs() 方法来计算两个浮点数之间差异的绝对值,如果这个差异在阈值范围之内,我们就认为两个浮点数是相等的。
1 2 3 4 5 6 7 8 9 10 final double THRESHOLD = .0001;   double d1 = .1 * 3; double d2 = .3;   if(Math.abs(d1-d2) < THRESHOLD) {     System.out.println("d1 和 d2 相等"); } else {     System.out.println("d1 和 d2 不相等"); }

Math.abs() 方法用来返回 double 的绝对值,如果 double 小于 0,则返回 double 的正值,否则返回 double。也就是说,abs() 后的结果绝对大于 0,如果结果小于阈值(THRESHOLD),我们就认为 d1 和 d2 相等。

  • 第二种方案是使用 BigDecimal 类,可以指定要舍入的模式和精度,这样就可以解决舍入的误差。

以使用 BigDecimal 类的 compareTo() 方法对两个数进行比较,该方法将会忽略小数点后的位数,怎么理解这句话呢?比如说 2.0 和 2.00 的位数不同,但它俩的值是相等的。

a.compareTo(b) 如果 a 和 b 相等,则返回 0,否则返回 -1。

 

tips: 不要使用 equals() 方法对两个 BigDecimal 对象进行比较,这是因为 equals() 方法会考虑位数,如果位数不同,则会返回 false,尽管数学值是相等的。

1 2 3 4 5 BigDecimal a = new BigDecimal("2.00"); BigDecimal b = new BigDecimal("2.0");   System.out.println(a.equals(b)); System.out.println(a.compareTo(b) == 0);

上面的代码中 a.equals(b) 的结果就为 false,因为 2.00 和 2.0 小数点后的位数不同,但 a.compareTo(b) == 0 的结果就为 true,因为 2.00 和 2.0 在数学层面的值的确是相等的。

compareTo() 方法比较的过程非常严谨,源码如下:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 private int compareMagnitude(BigDecimal val) {   // Match scales, avoid unnecessary inflation   long ys = val.intCompact;   long xs = this.intCompact;   if (xs == 0)     return (ys == 0) ? 0 : -1;   if (ys == 0)     return 1;     long sdiff = (long)this.scale - val.scale;   if (sdiff != 0) {     // Avoid matching scales if the (adjusted) exponents differ     long xae = (long)this.precision() - this.scale;  // [-1]     long yae = (long)val.precision() - val.scale;   // [-1]     if (xae < yae)       return -1;     if (xae > yae)       return 1;     if (sdiff < 0) {       // The cases sdiff <= Integer.MIN_VALUE intentionally fall through.       if ( sdiff > Integer.MIN_VALUE &&           (xs == INFLATED ||               (xs = longMultiplyPowerTen(xs, (int)-sdiff)) == INFLATED) &&           ys == INFLATED) {         BigInteger rb = bigMultiplyPowerTen((int)-sdiff);         return rb.compareMagnitude(val.intVal);       }     } else { // sdiff > 0       // The cases sdiff > Integer.MAX_VALUE intentionally fall through.       if ( sdiff <= Integer.MAX_VALUE &&           (ys == INFLATED ||               (ys = longMultiplyPowerTen(ys, (int)sdiff)) == INFLATED) &&           xs == INFLATED) {         BigInteger rb = val.bigMultiplyPowerTen((int)sdiff);         return this.intVal.compareMagnitude(rb);       }     }   }   if (xs != INFLATED)     return (ys != INFLATED) ? longCompareMagnitude(xs, ys) : -1;   else if (ys != INFLATED)     return 1;   else     return this.intVal.compareMagnitude(val.intVal); }

接下来,用 BigDecimal 来解决开头的问题。

1 2 3 4 5 6 7 8 9 BigDecimal d1 = new BigDecimal("0.1"); BigDecimal three = new BigDecimal("3"); BigDecimal d2 = new BigDecimal("0.3");   d1 = d1.multiply(three);   System.out.println("d1 = " + d1); System.out.println("d2 = " + d2); System.out.println(d1.compareTo(d2));

程序输出的结果如下:

1 2 3 d1 = 0.3 d2 = 0.3 0

d1 和 d2 都为 0.3,所以 compareTo() 的结果就为 0,表示两个值是相等的。

总结一下,在遇到浮点数的时候,千万不要使用 == 操作符来进行比较,因为有精度问题。要么使用阈值来忽略舍入的问题,要么使用 BigDecimal 来替代 double 或者 float。

以上就是Java正确比较浮点数的方法的详细内容,更多关于Java 正确比较浮点数的资料请关注脚本之家其它相关文章!

 

 

 

 

标签:Java,正确,double,浮点数,sdiff,d1,d2,BigDecimal
来源: https://www.cnblogs.com/kelelipeng/p/16638565.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有