Java Double 與 BigDecimal
1. 概述
Java 中[Double](https://docs.oracle.com/javase/7/docs/api/java/lang/Double.html)
與BigDecimal
之間的選擇會顯著影響效能以及浮點數的精確度和準確度。在本教程中,我們將比較和對比這兩個類別的特徵、優點和缺點、它們的用例,以及如何解決它們的精確度和舍入問題。
2. Double
Double
類別是double
基元資料類型的包裝器,非常適合通用浮點運算,並且在許多場景下都能很好地工作。然而,它有一些限制。最突出的問題是其有限的精度。由於二進位表示的性質, double
數在處理小數時可能會出現舍入錯誤。
例如, double
精度字面值 0.1 並不完全等於小數 0.1,而是稍大的值:
@Test
public void givenDoubleLiteral_whenAssigningToDoubleVariable_thenValueIsNotExactlyEqual() {
double doubleValue = 0.1;
double epsilon = 0.0000000000000001;
assertEquals(0.1, doubleValue, epsilon);
}
3. BigDecimal
BigDecimal
類別表示一個不可變的、任意精確度的、有符號的十進位數。它可以處理任何大小的數字而不會損失精度。想像一下,有一個強大的放大鏡,可以放大數軸的任何部分,使我們能夠處理大或極小的數字。
它由兩部分組成:未縮放的值(任意精確度的整數)和小數位數(表示小數點後的位數)。例如, BigDecimal
3.14 的未縮放值為 314,小數位數是 2。
BigDecimal
類別提供比Double
更好的精度,因為它可以使用任意精度的小數執行計算,避免了Double's
二進位表示形式產生的捨入誤差。這是因為BigDecimal
內部使用整數運算,這比浮點運算更準確。
讓我們來看看一些如何在 Java 中使用BigDecimal
類別的範例:
private BigDecimal bigDecimal1 = new BigDecimal("124567890.0987654321");
private BigDecimal bigDecimal2 = new BigDecimal("987654321.123456789");
@Test
public void givenTwoBigDecimals_whenAdd_thenCorrect() {
BigDecimal expected = new BigDecimal("1112222211.2222222211");
BigDecimal actual = bigDecimal1.add(bigDecimal2);
assertEquals(expected, actual);
}
@Test
public void givenTwoBigDecimals_whenMultiply_thenCorrect() {
BigDecimal expected = new BigDecimal("123030014929277547.5030955772112635269");
BigDecimal actual = bigDecimal1.multiply(bigDecimal2);
assertEquals(expected, actual);
}
@Test
public void givenTwoBigDecimals_whenSubtract_thenCorrect() {
BigDecimal expected = new BigDecimal("-863086431.0246913569");
BigDecimal actual = bigDecimal1.subtract(bigDecimal2);
assertEquals(expected, actual);
}
@Test
public void givenTwoBigDecimals_whenDivide_thenCorrect() {
BigDecimal expected = new BigDecimal("0.13");
BigDecimal actual = bigDecimal1.divide(bigDecimal2, 2, RoundingMode.HALF_UP);
assertEquals(expected, actual);
}
4. 比較和用例
4.1. Double
和BigDecimal
之間的比較
從Double
轉換為BigDecimal
相對簡單。 BigDecimal
類別提供接受double
值作為參數的建構子。但是,轉換並不能消除Double
的精度限制。相反,在擬合Double's
約束範圍時,從BigDecimal
轉換為Double
可能會導致資料遺失和捨入錯誤。
讓我們嘗試一個簡單的轉換:
@Test
void whenConvertingDoubleToBigDecimal_thenConversionIsCorrect() {
double doubleValue = 123.456;
BigDecimal bigDecimalValue = BigDecimal.valueOf(doubleValue);
BigDecimal expected = new BigDecimal("123.456").setScale(3, RoundingMode.HALF_UP);
assertEquals(expected, bigDecimalValue.setScale(3, RoundingMode.HALF_UP));
}
@Test
void whenConvertingBigDecimalToDouble_thenConversionIsCorrect() {
BigDecimal bigDecimalValue = new BigDecimal("789.123456789").setScale(9, RoundingMode.HALF_UP);
double doubleValue = bigDecimalValue.doubleValue();
double expected = new BigDecimal("789.123456789").setScale(9, RoundingMode.HALF_UP).doubleValue();
assertEquals(expected, doubleValue);
}
在速度和範圍方面, Java的Double
中使用硬體級浮點運算使其比BigDecimal
更快。 Double
艙涵蓋範圍廣泛,可容納大量和少量人數。然而,它被限制在 64 位元結構中,帶來了精度限制,特別是對於極大或極小的數字。相比之下, BigDecimal
提供了更廣泛的值範圍和更廣泛的值的更高精度。
記憶體使用情況也存在差異。 Java 的Double
更加緊湊,這導致記憶體使用效率更高。另一方面, BigDecimal's
優勢意味著更高的記憶體消耗。這可能會對我們的應用程式效能和可擴展性產生影響,特別是在記憶體密集型環境中。
4.2.用例
Double
可以輕鬆地與其他數位類型連接,使其成為基本算術的便利選擇。當性能是優先考慮的時候,它是首選。 Double's
速度和記憶體效率使其成為圖形和遊戲開發等應用程式的可靠選擇,這些應用程式通常涉及即時渲染和複雜的視覺效果。在這裡,效能對於維持流暢的使用者體驗至關重要。
另一方面, BigDecimal
在處理貨幣計算時表現出色,其中精度錯誤可能會導致重大財務損失。它也是需要絕對精確度的科學模擬的救世主。雖然BigDecimal
可能速度較慢且佔用記憶體較多,但它在準確性方面提供的保證在關鍵場景中可能非常寶貴。
因此, BigDecimal
更適合金融應用、科學模擬、工程和物理模擬、數據分析和報告以及其他精度至關重要的領域中的任務。
4.3.精度和捨入註意事項
使用BigDecimal
,我們可以決定我們的計算將有多少位小數。當我們需要精確的十進制計算時,這非常有用,因為它可以按原樣儲存每個十進制數字。
我們也可以選擇計算中舍入的方式。不同的捨入模式對我們的結果有不同的影響,例如:
-
UP
:將數字增加到下一個較高的值(當我們想要確保一個值永遠不會小於某個值時) -
DOWN
:將數字減少到前面的較低值(當我們想要確保一個值永遠不會大於某個特定值時) -
HALF_UP
:如果丟棄的分數大於 0.5,則向上捨入 -
HALF_DOWN
:如果丟棄的分數小於 0.5,則向下舍入
當我們需要準確和統一的數據時,這種對舍入和精度的控制水平是BigDecimal
更適合金融計算的另一個原因。
由於電腦表示數字的方式, Double
引入了出現微小錯誤的可能性。表示重複小數(例如 1/3)可能會很棘手,因為它們會導致無限的二進位展開。
當我們嘗試用二進位(基數 2)表示像 0.1 這樣的簡單數字時,它們也會變得同樣混亂。我們會得到像 0.00011001100110011 這樣的重複分數…計算機表示這些分數的位數有限,因此必須在某個時刻將它們四捨五入。因此,儲存的值並不完全是 0.1,這可能會在我們執行計算時導致微小的錯誤。
4.4.比較表
讓我們用表格總結一下我們對Double
與BigDecimal
的了解:
方面 | **Double** |
**BigDecimal** |
---|---|---|
精確 | 有限的 | 隨意的 |
範圍 | 廣泛(大和小) | 廣泛的 |
記憶體使用情況 | 袖珍的 | 更高 |
表現 | 快點 | 慢點 |
用例 | 一般用途 | 金融、科學 |
5. 結論
在本文中,我們討論了 Java Double
和BigDecimal
類型之間的細微差別以及使用它們時精度和性能之間的權衡。
與往常一樣,程式碼範例可在 GitHub 上取得。