2011-08-10 185 views
5

對於下面的代碼(JAVA):Java中如何將double轉換爲int?

double d = (double) m/n; //m and n are integers, n>0 
int i = (int) (d * n); 

i == m 

總是最後一個表達式是真的嗎? 如果不是這總是正確的?:

i = (int) Math.round(d * n); 

i == m 
+1

不是一個愚蠢的問題;這個問題引發了一些關於浮點算法和整數「可恢復性」的微妙問題。 – Nayuki

+0

最近這個網站上有很多浮點問題 - 嗯... – 2011-08-10 17:56:23

回答

4

第二個問題,你問的關注如何ulp大是Java。

如果ULP超過1/(n),然後舍入乘法不會恢復原始分INT。通常,較大的ulps與較大的double值相關聯。與double相關的ulp在9E15附近開始超過1;如果你的恢復雙打在那裏,那麼你可能會發現問題與輪()沒有得到預期的答案。但是,在使用int值時,分區的最大分子值將爲Integer.MAX_VALUE

下面的程序測試所有的n正整數,看看哪一個會導致試圖恢復被分割的INT當舍入誤差最大的潛力:

public static void main(String[] args) 
    { 
    // start with large number 
    int m = Integer.MAX_VALUE; 
    double d = 0; 

    double largestError = 0; 
    int bigErrorCause = -1; 
    for (int n = 1; n < Integer.MAX_VALUE; n++) 
    { 
     d = (double) m/n; 
     double possibleError = Math.ulp(d) * n; 
     if (possibleError > largestError) 
     { 
     largestError = possibleError; 
     bigErrorCause = n; 
     } 
    } 
    System.out.println("int " + bigErrorCause + " causes at most " 
     + largestError + " error"); 
    } 

輸出:

int 1073741823最多導致4。768371577590358E-7錯誤

舍入使用Math.round,然後轉換爲int應該恢復原始的int。

1

在數學上它應該是正確的。但是,您可能會得到浮點舍入錯誤,這會使其失敗。您幾乎不應該使用==來比較浮點精度數字。

你好得多使用這樣的門檻比較它們:

Math.abs(d*n - m) < 0.000001; 

注意這兩種說法應該是等價的

i = (int) (d * n); 
i = (int) Math.round(d * n); 

但是例如,如果d=3/2n=2,浮動可能會導致i=2.999999999999點誤差,這在截去後/舍入爲2

+3

你對截斷的推理很好。但是你的例子很糟糕,因爲浮點除以2總是精確的(除了下溢)。 ;-) – Nayuki

1

第一上是d無限期地不總是真實的。第二個我會說是的,這是真的,但只是因爲我想不出一個反例。

如果n是非常大的,它可能是假的,我不知道真的。我知道至少有99%的時間會是真的。

5

int i = (int) (d * n); i == m;

這是對於m = 1爲假,N = 49。

i = (int) Math.round(d * n); i == m;

我的直覺告訴我,它應該是真實的,但它可能是難以嚴格證明。

+0

即使在JavaScript中,您也可以驗證我的聲明。你可以在瀏覽器中輸入:'javascript:1/49 * 49',它給出0.9999 ...。 – Nayuki

+2

+1由於'double'具有53位的精度,並且將它乘以小於2^31,結果應該小於1/2^21 = 2 * 1/2^22( 2的因素來自做兩個操作)。所以四捨五入將會大幅度地確定整數。 – starblue

+0

我刪除了我的帖子,因爲我認爲我錯了第二種情況,尼斯答案(我已經給你我的+1) – MByD