看下面这段代码,将 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 类的 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 正确比较浮点数的资料请关注脚本之家其它相关文章!