java大量浮点数如何作比较,Java如何正确比较浮点数
看下面這段代碼,將 d1 和 d2 兩個(gè)浮點(diǎn)數(shù)進(jìn)行比較,輸出的結(jié)果會(huì)是什么?
double d1 = .1 * 3;
double d2 = .3;
System.out.println(d1 == d2);
按照正常邏輯來(lái)看,d1經(jīng)過(guò)計(jì)算之后的結(jié)果應(yīng)該是0.3,最后打印的結(jié)果應(yīng)該是 true,對(duì)吧?但是運(yùn)行一下就會(huì)發(fā)現(xiàn)結(jié)果并不是 true 而是 false 。
輸出一下 d1,發(fā)現(xiàn)得到的答案不是想象中的 0.3 而是 0.30000000000000004,所以和 d2 進(jìn)行比較結(jié)果自然是 false
如何正確地比較浮點(diǎn)數(shù)(單精度的 float 和雙精度的 double),不單單是 Java 特定的問(wèn)題,在計(jì)算機(jī)的內(nèi)存中,存儲(chǔ)浮點(diǎn)數(shù)時(shí)使用的是 IEEE 754 標(biāo)準(zhǔn),就會(huì)有精度的問(wèn)題。
存儲(chǔ)和轉(zhuǎn)換的過(guò)程中浮點(diǎn)數(shù)容易引起一些較小的舍入誤差,正是這個(gè)原因,導(dǎo)致在比較浮點(diǎn)數(shù)的時(shí)候,不能使用“==”操作符——要求嚴(yán)格意義上的完全相等。
那么如何正確的比較浮點(diǎn)數(shù)呢?這里有兩種方案。
第一種方案是允許兩個(gè)值之間存在一點(diǎn)誤差(指定一個(gè)閾值),使用 Math.abs() 方法來(lái)計(jì)算兩個(gè)浮點(diǎn)數(shù)之間差異的絕對(duì)值,如果這個(gè)差異在閾值范圍之內(nèi),我們就認(rèn)為兩個(gè)浮點(diǎn)數(shù)是相等的。
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() 方法用來(lái)返回 double 的絕對(duì)值,如果 double 小于 0,則返回 double 的正值,否則返回 double。也就是說(shuō),abs() 后的結(jié)果絕對(duì)大于 0,如果結(jié)果小于閾值(THRESHOLD),我們就認(rèn)為 d1 和 d2 相等。
第二種方案是使用 BigDecimal 類,可以指定要舍入的模式和精度,這樣就可以解決舍入的誤差。
以使用 BigDecimal 類的 compareTo() 方法對(duì)兩個(gè)數(shù)進(jìn)行比較,該方法將會(huì)忽略小數(shù)點(diǎn)后的位數(shù),怎么理解這句話呢?比如說(shuō) 2.0 和 2.00 的位數(shù)不同,但它倆的值是相等的。
a.compareTo(b) 如果 a 和 b 相等,則返回 0,否則返回 -1。
tips: 不要使用 equals() 方法對(duì)兩個(gè) BigDecimal 對(duì)象進(jìn)行比較,這是因?yàn)?equals() 方法會(huì)考慮位數(shù),如果位數(shù)不同,則會(huì)返回 false,盡管數(shù)學(xué)值是相等的。
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) 的結(jié)果就為 false,因?yàn)?2.00 和 2.0 小數(shù)點(diǎn)后的位數(shù)不同,但 a.compareTo(b) == 0 的結(jié)果就為 true,因?yàn)?2.00 和 2.0 在數(shù)學(xué)層面的值的確是相等的。
compareTo() 方法比較的過(guò)程非常嚴(yán)謹(jǐn),源碼如下:
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);
}
接下來(lái),用 BigDecimal 來(lái)解決開(kāi)頭的問(wèn)題。
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));
程序輸出的結(jié)果如下:
d1 = 0.3
d2 = 0.3
0
d1 和 d2 都為 0.3,所以 compareTo() 的結(jié)果就為 0,表示兩個(gè)值是相等的。
總結(jié)一下,在遇到浮點(diǎn)數(shù)的時(shí)候,千萬(wàn)不要使用 == 操作符來(lái)進(jìn)行比較,因?yàn)橛芯葐?wèn)題。要么使用閾值來(lái)忽略舍入的問(wèn)題,要么使用 BigDecimal 來(lái)替代 double 或者 float。
總結(jié)
以上是生活随笔為你收集整理的java大量浮点数如何作比较,Java如何正确比较浮点数的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 高通cpu排行_安卓手机芯片排行:麒麟9
- 下一篇: svn php改客户端密码_记录Visu