在Java中比較Double
- java
1.概述
在本教程中,我們將討論在Java中比較雙精度值的不同方法。特別是,它不像比較其他原始類型那樣容易。事實上,它在許多其他語言中都是有問題的,不僅是Java。
首先,我們將解釋為什麼使用簡單的==運算符不准確,並且可能導致在運行時難以跟踪錯誤。然後,我們將展示如何正確比較普通Java庫和常見第三方庫中的double。
2.使用==運算符
使用==運算符進行比較時的不准確性是由於將雙精度值存儲在計算機內存中的方式引起的。我們需要記住,在有限的內存空間(通常為64位)中必須容納無數個值。結果,我們無法在計算機中精確表示大多數double值。必須將它們四捨五入以保存。
由於四捨五入的準確性,可能會發生有趣的錯誤:
double d1 = 0;
for (int i = 1; i <= 8; i++) {
d1 += 0.1;
}
double d2 = 0.1 * 8;
System.out.println(d1);
System.out.println(d2);
d1
和d2,
這兩個變量均應等於0.8。但是,當我們運行上面的代碼時,我們將看到以下結果:
0.7999999999999999
0.8
在這種情況下,將兩個值與==運算符進行比較將產生錯誤的結果。因此,我們必須使用更複雜的比較算法。
如果我們想獲得最佳精度並控制舍入機制,則可以使用java.math.BigDecimal
類。
3.在純Java中比較雙打
在純Java中比較雙值的推薦算法是閾值比較方法。在這種情況下,我們需要檢查兩個數字之間的差異是否在指定的公差範圍內,通常稱為**epsilon** :
double epsilon = 0.000001d;
assertThat(Math.abs(d1 - d2) < epsilon).isTrue();
epsilon值越小,比較精度越高。但是,如果我們指定的公差值太小,則會得到與簡單==比較中相同的錯誤結果。通常, epsilon的小數點後5位和6位的值通常是一個很好的起點。
不幸的是,標準JDK中沒有實用程序可用於以推薦的精確方式比較雙精度值。幸運的是,我們不需要自己編寫。我們可以使用免費的和廣為人知的第三方庫提供的各種專用方法。
4.使用Apache Commons Math
Apache Commons Math是致力於數學和統計組件的最大的開源庫之一。從各種不同的類和方法中,我們將org.apache.commons.math3.util.Precision
類。它包含2個有用的equals()
方法來正確比較雙精度值:
double epsilon = 0.000001d;
assertThat(Precision.equals(d1, d2, epsilon)).isTrue();
assertThat(Precision.equals(d1, d2)).isTrue();
這裡使用的epsilon
變量與前面的示例具有相同的含義。這是允許的絕對誤差量。但是,這並不是與閾值算法的唯一相似之處。特別是,兩種equals
方法在後台使用相同的方法。
兩參數函數版本只是equals(d1, d2, 1)
方法調用的快捷方式。該版本中的epsilon值很高。因此,我們不應該使用它,而總是自己指定公差值。
5.使用番石榴
Google的Guava是一大堆核心Java庫,它們擴展了標準JDK功能。 com.google.common.math
軟件包中包含大量有用的數學工具。為了在Guava中正確比較double值,讓我們實現DoubleMath
類中**fuzzyEquals()
**方法:
double epsilon = 0.000001d;
assertThat(DoubleMath.fuzzyEquals(d1, d2, epsilon)).isTrue();
方法名稱與Apache Commons Math中的方法名稱不同,但實際上在後台運行相同。唯一的區別是沒有使用epsilon的默認值的重載方法。
6.使用JUnit
JUnit是Java中使用最廣泛的單元測試框架之一。通常,每個單元測試通常以分析預期值與實際值之間的差異結束。因此,測試框架必須具有正確而精確的比較算法。實際上,JUnit提供了一組用於常見對象,集合和原始類型的比較方法,包括專用的方法來檢查雙精度值是否相等:
double epsilon = 0.000001d;
assertEquals(d1, d2, epsilon);
實際上,它的工作原理與之前描述的Guava和Apache Commons的方法相同。
重要的是要指出,還有一個不推薦使用的包含兩個參數的版本,沒有epsilon參數。但是,如果要確保結果始終正確,則應堅持使用三參數版本。
7.結論
在本文中,我們探索了在Java中比較雙精度值的不同方法。
我們已經解釋了為什麼簡單比較可能導致在運行時難以跟踪錯誤。然後,我們展示瞭如何正確地比較普通Java庫和通用庫中的值。