Java正确比较浮点数的方法
看下面这段代码,将d1和d2两个浮点数进行比较,输出的结果会是什么?
doubled1=.1*3; doubled2=.3; System.out.println(d1==d2);
按照正常逻辑来看,d1经过计算之后的结果应该是0.3,最后打印的结果应该是true,对吧?但是运行一下就会发现结果并不是true而是false。
输出一下d1,发现得到的答案不是想象中的0.3而是0.30000000000000004,所以和d2进行比较结果自然是false
如何正确地比较浮点数(单精度的float和双精度的double),不单单是Java特定的问题,在计算机的内存中,存储浮点数时使用的是IEEE754标准,就会有精度的问题。
存储和转换的过程中浮点数容易引起一些较小的舍入误差,正是这个原因,导致在比较浮点数的时候,不能使用“==”操作符——要求严格意义上的完全相等。
那么如何正确的比较浮点数呢?这里有两种方案。
- 第一种方案是允许两个值之间存在一点误差(指定一个阈值),使用Math.abs()方法来计算两个浮点数之间差异的绝对值,如果这个差异在阈值范围之内,我们就认为两个浮点数是相等的。
finaldoubleTHRESHOLD=.0001; doubled1=.1*3; doubled2=.3; if(Math.abs(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,尽管数学值是相等的。
BigDecimala=newBigDecimal("2.00"); BigDecimalb=newBigDecimal("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()方法比较的过程非常严谨,源码如下:
privateintcompareMagnitude(BigDecimalval){ //Matchscales,avoidunnecessaryinflation longys=val.intCompact; longxs=this.intCompact; if(xs==0) return(ys==0)?0:-1; if(ys==0) return1; longsdiff=(long)this.scale-val.scale; if(sdiff!=0){ //Avoidmatchingscalesifthe(adjusted)exponentsdiffer longxae=(long)this.precision()-this.scale;//[-1] longyae=(long)val.precision()-val.scale;//[-1] if(xaeyae) return1; if(sdiff<0){ //Thecasessdiff<=Integer.MIN_VALUEintentionallyfallthrough. if(sdiff>Integer.MIN_VALUE&& (xs==INFLATED|| (xs=longMultiplyPowerTen(xs,(int)-sdiff))==INFLATED)&& ys==INFLATED){ BigIntegerrb=bigMultiplyPowerTen((int)-sdiff); returnrb.compareMagnitude(val.intVal); } }else{//sdiff>0 //Thecasessdiff>Integer.MAX_VALUEintentionallyfallthrough. if(sdiff<=Integer.MAX_VALUE&& (ys==INFLATED|| (ys=longMultiplyPowerTen(ys,(int)sdiff))==INFLATED)&& xs==INFLATED){ BigIntegerrb=val.bigMultiplyPowerTen((int)sdiff); returnthis.intVal.compareMagnitude(rb); } } } if(xs!=INFLATED) return(ys!=INFLATED)?longCompareMagnitude(xs,ys):-1; elseif(ys!=INFLATED) return1; else returnthis.intVal.compareMagnitude(val.intVal); } 接下来,用BigDecimal来解决开头的问题。
BigDecimald1=newBigDecimal("0.1"); BigDecimalthree=newBigDecimal("3"); BigDecimald2=newBigDecimal("0.3"); d1=d1.multiply(three); System.out.println("d1="+d1); System.out.println("d2="+d2); System.out.println(d1.compareTo(d2));程序输出的结果如下:
d1=0.3 d2=0.3 0d1和d2都为0.3,所以compareTo()的结果就为0,表示两个值是相等的。
总结一下,在遇到浮点数的时候,千万不要使用==操作符来进行比较,因为有精度问题。要么使用阈值来忽略舍入的问题,要么使用BigDecimal来替代double或者float。
以上就是Java正确比较浮点数的方法的详细内容,更多关于Java正确比较浮点数的资料请关注毛票票其它相关文章!