2016-11-04 52 views
2

今天我發現一個有趣的事實,即公式會影響結果的精度。請看下面的代碼Java雙精度:不同的公式會導致不同的結果

double x = 7d 
double y = 10d 
​println(1-x/y​) 
println((y-x)/y​)​ 

我寫了使用groovy這個代碼,你可以把它當作Java

結果是

1-x/y: 0.30000000000000004 
(y-x)/y: 0.3 

有趣的是,這兩個公式應該是平等的有不同的結果。

  • 任何人都可以爲我解釋嗎?
  • 我可以將第二個公式應用於適用於雙精度問題的有效解決方案嗎?
+1

這很重要http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html – DejaVuSansMono

+0

嘗試打印出所有中間結果。其中一個可能稍微偏離了原因,因爲它不能用base-2精確表示。 – Thilo

+0

毫不奇怪:一個極端的例子是'1-sqr(cos(x))',其中sqr表示平方。這在數學上等於'sqr(sin(x))'。現在嘗試評估這些公式爲'x'接近0。 – Henry

回答

0

要控制浮點運算的精度,應該使用java.math.BigDecimal。 你可以做這樣的事情。

BigDecimal xBigdecimal = BigDecimal.valueOf(7d); 
BigDecimal yBigdecimal = BigDecimal.valueOf(10d); 
System.out.println(BigDecimal.valueOf(1).subtract(xBigdecimal.divide(yBigdecimal))); 

誰能解釋一下嗎?

Java中的float和double基元類型是浮點數,其中數字以分數和指數的二進制表示形式存儲。

更具體地,一個雙精度浮點值如雙類型是64位的值,其中:

1位表示的符號(正或負)。 指數的11位。 有效位數爲52位(小數部分爲二進制)。 這些部分組合起來產生一個值的雙重表示。

檢查this

對於浮點值如何在Java處理的詳細說明,請浮點類型,格式和Java語言規範的價值觀。

byte,char,int,long類型是定點數,它們是數字的精確表示。與定點數字不同,浮點數字有時會(在大多數情況下是安全的)不能返回數字的精確表示。這就是爲什麼在1 - (x/y)的結果中最終得到0.30000000000000004的原因。

當需要確切的值(如1.5或150.1005)時,您需要使用其中一種定點類型,它將能夠精確地表示數字。

正如我在上面的例子中已經展示的,Java有一個BigDecimal類,它可以處理非常大的數字和非常小的數字。