2014-01-15 63 views
1

這幾天,我正在處理一些關於ACM-ICPC的問題(雖然我已經畢業了..只是爲了好玩..)。昨天,我幾乎變得瘋了,因爲有一位在線評委總是說我寫的代碼是「錯誤的回答」。最後,經過十多個可怕的10個小時後,我意識到以下聲明是原因。這些陳述之間有什麼區別?

int target = (int)((double)(M * 100)/N) + 1;   // RIGHT!! 
    int target = (int)((double) M/N * 100) + 1;   // WRONG!! 

我無法確切地知道第一個語句的行爲與第二個語句的行爲不同。因爲我不允許看到法官使用的測試用例,所以我很難理解代碼何時會出錯。有沒有人可以向我解釋?謝謝。

              *我使用Java。

+0

M和N的類型是什麼? – Valentin

+0

你是什麼意思,錯誤,你是否得到一個錯誤,或者它給出了一個不同的答案解釋?還有什麼是M和N的數據類型。 – Deepak

+0

@Valentin哦,我錯過了。 M和N的類型很長。 – gwpark

回答

2

據我所知,這兩個表達式

(double)(M * 100)/N 
(double) M/N * 100 

的結果是除了浮點精度誤差(也爲可能的溢出是相同的,但讓我們忽略了他們在這裏,因爲這兩條線爲儘管採用了不同的方式)。這些錯誤可能導致的值是一個稍微高於或等於一個整數,和一個稍低於相同的整數,這將導致

(int)((double)(M * 100)/N) 
(int)((double) M/N * 100) 

由一個不同。一般來說,在處理浮點時,如果您將該部門作爲最後一個操作,則您有更多機會接近「實際」值。

有一個進一步的考慮,這可能會非常棘手:你的第二行中沒有圍繞(double)M/N的括號。這可能會給優化器帶來額外的自由度,這可能會使結果依賴於優化級別。我不知道這是否會發生在Java中。

至於操作的順序,我嘗試了用C這種特殊情況下(因爲這是對我更快):

int i, j, k; 
for (i = 1; i <= 100; i++) { 
    j = (int)(((double)i/100) * 100); 
    if (i != j) { 
     printf("%d -> %d\n", i, j); 
    } 
    k = (int)(((double)i * 100)/100); 
    if (i != k) { 
     printf("%d ?? %d\n", i, k); 
    } 
} 

和我的機器上輸出

29 -> 28 
57 -> 56 
58 -> 57 

更換100 10000000產生同樣類型的587200行(即錯誤率5.872%)

+0

我很感謝你的解釋。我應該嘗試離開浮動號碼操作作爲最後的操作。但是...對不起,我還有一個問題。當我還是大學生時,教授曾經說過,Fortran可以保證浮動數字操作的準確性。但是昨天有人認爲這是完全錯誤的。他說,Fortan也使用IEEE 754作爲其他編程語言,並且在EVERYWHERE中原始的浮點數操作(除了Java中的BigDecial之類的包裝器)必須是不準確的。你怎麼看待他的說法?再次,謝謝你,並打擾你打斷你。 – gwpark

+0

@gwpark,儘管有些操作對於某些輸入具有完全的準確性,例如,具有2的冪的'*'和'/'或兩個整數的「*」小於浮點數的有效位數(最有可能的情況與'M * 100'一樣)。但是你從一個錯誤的陳述開始,因爲,例如,'(double)(M * 100)/ N'和'(double)M * 100/N'有一個整數'*'第一個和一個FP'*'第二,但實際上總會產生相同的結果 - 它沒有'/'最後可以給出不同的結果 –