2011-12-06 21 views
2

我知道double/float的問題,建議使用BigDecimal而不是double/float來表示貨幣字段。但雙重/浮動更有效,節省空間。然後我的問題是: 可以使用double/float來表示貨幣字段在Java類中,但是使用BigDecimal來處理算術(即在任何算術之前將double/float轉換爲BigDecimal)並進行相等檢查?我們可以使用double來存儲貨幣字段並使用BigDecimal進行算術

原因是爲了節省一些空間。我真的看到很多項目都使用double/float來表示貨幣領域。

這有什麼缺陷嗎? 在此先感謝。

+0

除非你喜歡有錢消失,否則不要在浮動/雙打中存儲貨幣值。 –

+0

由於我只能接受一個答案,非常感謝你們所有人。 – lostinmoney

回答

5

不,你不能。

假設double足以存儲兩個值xy。然後你將它們轉換爲安全的BigDecimal並將其倍數。結果是準確的,但是如果您將乘法結果存回double,您很可能會失去精度。證明:

double x = 1234567891234.0; 
double y = 1234567891234.0; 
System.out.println(x); 
System.out.println(y); 

BigDecimal bigZ = new BigDecimal(x).multiply(new BigDecimal(y)); 
double z = bigZ.doubleValue(); 
System.out.println(bigZ); 
System.out.println(z); 

結果:

1.234567891234E12   //precise 'x' 
1.234567891234E12   //precise 'y' 
1524157878065965654042756 //precise 'x * y' 
1.5241578780659657E24  //loosing precision 

xy是準確的,以及使用BigDecimal乘法。然而,在回到double之後,我們將失去最低有效位數。

+0

非常感謝。那麼你的建議是哪一個? 1.對所有字段使用BigDecimal。 2.使用long來表示這些字段。 – lostinmoney

+0

'double'允許您存儲多達16位最重要的數字,'long' - 從0開始的18位數字。'double'可能會丟失最小有效數字而不會發出警告,另一方面'long'可能會溢出。如果你真的關心準確性和可靠性,總是使用'BigDecimal'(除非你真的**事先知道數值的上限和下限)。畢竟,擁有快速或正確的程序會更好嗎? –

+1

如果您將一美元金額乘以另一美元金額,則您的問題比您選擇的數據類型更大。 。 。 – ruakh

2

long將會比double/float好得多。

您確定使用BigDecimal類型會是一個真正的瓶頸嗎?

+0

由於這不是一個簡單的貨幣應用程序,只處理美分。我們需要保留至少5個小數點的價格/金額。 – lostinmoney

+0

+1。但要澄清:如果你使用int或long,你必須使用某種常數因子(通常爲100或1000)來支持少於一個貨幣單位。例如,您可能將1美元表示爲1000L,1美分表示爲10L。將它封裝在自己的數據類型中是一個好主意。 – ruakh

0

坑坑窪窪的是,漂浮/雙打不能保存所有的數值而不會失去精確度。即使您使用BigDecimal並在計算過程中保持精度,您仍然將最終產品存儲爲浮點/雙精度。

根據我的經驗,「適當」解決方案是將貨幣價值存儲爲代表數千美元的整數(例如Long)。這爲大多數任務提供了足夠的分辨率,興趣增加,同時側面使用浮動/雙打的問題。作爲額外的「獎勵」,這需要大約相同數量的存儲作爲浮動/雙打。

2

什麼是可以接受的取決於你的項目。在某些項目中,你可以使用double和long來做這件事。但在其他項目中,這被認爲是不可接受的。作爲一個雙倍你可以代表價值高達70,000,000,000,000.00美分(大於美國國債),固定的地方長期可以準確地代表90,000,000,000,000,000.00。

如果您必須處理高通貨膨脹的貨幣(無論如何都是壞主意),但出於某種原因仍需要考慮每一分,請使用BigDecimal。

如果使用double或long或BigDecimal,則必須舍入結果。你如何做到這一點因數據類型而異,BigDecimal是最不容易出錯的,因爲你需要指定不同操作的舍入和精度。有了雙倍或者更長的時間,你可以留到自己的設備上。

2

我也建議你使用除BigDecimal以外的所有可能涉及貨幣的算術。

確保您始終使用BigDecimal的String構造函數。爲什麼?試試下面的代碼在JUnit測試:

assertEquals(new BigDecimal("0.01").toString(), new BigDecimal(0.01).toString()); 

會得到以下的輸出:

expected:<0.01[]> but was <0.01[000000000000000020816681711721685132943093776702880859375]> 

事實是,你不能存儲EXACTLY 0.01作爲「雙」量。只有BigDecimal根據需要存儲您需要的數字正確

並記住BigDecimal是不可變的。下面將編譯:

BigDecimal amount = new BigDecimal("123.45"); 
BigDecimal more = new BigDecimal("12.34"); 
amount.add(more); 
System.out.println("Amount is now: " + amount); 

但產生的結果應該是:現在

金額爲:123.45

那是因爲你需要將結果分配到一個新的(或相同)BigDecimal變量。

換句話說:

amount = amount.add(more) 
+0

非常感謝您對BigDecimal的最佳實踐。 – lostinmoney

0

如果double的唯一用途是十進制值,那麼,就可以在一定條件下:如果你能保證你的價值觀有不超過15十進制數字,然後將值轉換爲double(53位精度),並將double轉換回十進制,精度爲15位(或更低),這將爲您提供David Matula定理應用程序的原始值,即沒有任何損失證明在他的文章In-and-out conversions。請注意,爲了使此結果適用,轉換必須使用correct rounding完成。

注意然而,一個double未必是最佳選擇:貨幣值一般表示未在浮點,但在固定點值的小數點後的數位(p),並且在這種情況下,轉換將該值整定爲具有縮放10^p並存儲此整數(如其他人所建議的)的整數更好。

相關問題