2012-08-14 244 views
58

我正在用BigDecimal執行一個簡單的乘法,當乘以零(在這個用例中乘以零是正確的)時,我發現了一些奇怪的行爲。BigDecimal乘以零

基本的數學告訴我,任何事情乘以零將等於零(參見:Zero Product PropertyMultiplication Properties

但是,下面的代碼將始終失敗,相同的錯誤:

assertEquals(new BigDecimal(0), new BigDecimal(22.3).multiply(new BigDecimal(0))); 
java.lang.AssertionError: 
Expected :0 
Actual :0E-48 

這是BigDecimal的一個不準確的地方,還是我在某處丟失了一些數學分支?

注:JDK 1.6.0_27中的IntelliJ 11

+3

是考慮數值分析,特別是逼近和截斷誤差 – 2012-08-14 09:26:31

+0

或者在'double'你可以寫'assertEquals(0,23.3 * 0,0);';) – 2012-08-14 09:29:01

+11

並且查看'BigDecimal.ZERO'。 – EJP 2012-08-14 10:41:14

回答

84

運行,則不能使用equals()方法比較BigDecimals,這樣的說法呢。那是因爲這個等於函數會比較的規模。如果比例不同,equals()將返回false,即使它們在數學上是相同的數字。

但是,您可以使用compareTo()做你想做什麼:

由於@assylias指出的那樣,你也應該使用new BigDecimal("22.3")構造,以避免重複精度的問題。

BigDecimal expected = BigDecimal.ZERO; 
BigDecimal actual = new BigDecimal("22.3").multiply(BigDecimal.ZERO); 
assertEquals(0, expected.compareTo(actual)); 

還有一種稱爲signum()方法,即返回-1,0或1爲負,零和正。所以,你也可以測試爲零

assertEquals(0, actual.signum()); 
20

equals()BigDecimal檢查BigDecimal的內部狀態比較

請參考下

public boolean equals(Object x) { 
    if (!(x instanceof BigDecimal)) 
     return false; 
    BigDecimal xDec = (BigDecimal) x; 
    if (x == this) 
     return true; 
    if (scale != xDec.scale) 
     return false; 
    long s = this.intCompact; 
    long xs = xDec.intCompact; 
    if (s != INFLATED) { 
     if (xs == INFLATED) 
      xs = compactValFor(xDec.intVal); 
     return xs == s; 
    } else if (xs != INFLATED) 
     return xs == compactValFor(this.intVal); 

    return this.inflate().equals(xDec.inflate()); 
} 

的代碼,如果要比較的值,用compareTo()

將代碼改爲

assertEquals(0 , new BigDecimal(0).compareTo(new BigDecimal(22.3).multiply(new BigDecimal(0))); 

更新:

使用構造函數以字符串作爲參數爲BigDecimal的精度精密檢查以下


相關鏈接請參見

47

有2個問題,你的代碼:

  • 你應該的compareTo,而不是平等比較的BigDecimal,通過其他的答案
  • 的建議,但你也應該使用字符串構造函數:new BigDecimal("22.3")而不是雙重構造函數new BigDecimal(22.3)以避免雙精度問題

換句話說,下面的代碼(它正確地使用的compareTo)仍返回false:

BigDecimal bd = new BigDecimal(0.1).multiply(new BigDecimal(10)); 
System.out.println(bd.compareTo(BigDecimal.ONE) == 0); 

因爲0.1d * 10d != 1

+0

感謝您指出構造函數更改 – Richard 2012-08-14 09:40:00

+0

@assylias「,但您還應該使用字符串構造函數:new BigDecimal(」22.3「),而不是雙重構造函數new BigDecimal(22.3)以避免雙精度問題。你能解釋雙精度問題嗎? – Geek 2012-08-16 07:49:16

+1

@Geek試試這個'System.out.println(新的BigDecimal(「0.1」));'和這個'System.out.println(新的BigDecimal(0.1d));'。第一個(String構造函數)打印'0.1',而第二個(雙構造函數)打印'0.1000000000000000055511151231257827021181583404541015625'。發生這種情況是因爲double不能準確地表示0.1。關於它的更多信息[例如在此頁上](http:// stackoverflow。COM /問題/ 4937402 /移動-小數位,在功能於一個雙)。 – assylias 2012-08-16 08:26:48