由一段會被人嘲笑的代碼想到的


這是一個關於數據溢出的問題,如果你對這個知識點感興趣請繼續往下看。引入問題之前我們先溫習一下溢出的概念:一個算數運算溢出,是指完整的整數結果不能放到數據類型的字長限制中去。

問題:寫出一個具有如下原型的函數,如果參數x 和 y 相加不會產生溢出,這個函數就返回 1.

int tadd_ok(int x,int y);

錯誤的解決辦法:

1 /* WARNING: This code is buggy. */
2 boolean tadd_ok(int x,int y){
3     int sum=x+y;
4     return (sum-x == y) && (sum-y == x);
5 }

這段代碼的思路是,如果發生了溢出 (x+y)-y 的結果就不會等於x,且 (x+y)-x 的結果也不會等於y.但實際測試結果是,函數 tadd_ok() 的返回值總是 true。為什么會產生這樣的結果呢?下面先來看另一段代碼:

public class demo01 {
    public static void main(String[] args) {
        int sum=2147483647+3;       //IntMax:2147483647
        System.out.println("sum="+sum);
        System.out.println("sum-3="+(sum-3));
    }

}
//output:
sum = -2147483646
sum-3 = 2147483647    

  從上面代碼中看到,2147483647+3 后發生了溢出,但是溢出后的值減3后又得到了原來的值2147483647。這看起來似乎不合常理,因為發生溢出后,超過數據類型字長限制的部分會被丟棄,這樣運算的結果就不會是我們預期的值,而且逆運算之后也更加不會得到原來的初值,但實際情況卻不是這樣的。

  為了更直觀地解釋這種“詭異”的現象,我們假設機器字長為4位,x=9 和 x=12 的位表示分別為 [1001] 和 [1100],它們的和是21 (5位數字表示為 [10101]),但是機器位只有4位,於是最高位會被丟棄,我們就得到 [0101],也就是十進制的5.我們看到,在 x+y 的運算過程中發生了溢出,導致運算結果5與預期值21不符,但是模擬上面的程序,再進行逆運算會發生什么呢?即運算結果5減去9,如果5-9的結果等於12就說明與上面的程序運行結果相匹配,我們不妨仔細看看運算過程:

      5-9 = 5 + (-9) = [0101] + [0111] = [1100] =12.

  上面的計算過程可能還不夠清晰,'-9'的補碼為什么是 [0111],[0111] 不是 7 的補碼嗎?難道說 -9 =7 嗎?在這里給出兩種解釋方式:(這僅僅是我認為正確的思路,標准答案是什么還望高手不吝賜教)

    第一種解釋:把 [0111] 並不是 '-9' 的補碼,而是表達式 (-9) 的4位二進制表示。我們知道,對於任意整數值 x ,計算表達式 -x 和 ~x+1 得到的結果完全一樣。利用這個結論,我們可以得到 '-9' 的位表示推理過程 [1001] 取反得到 [0110], 加1得 [0111].

    第二種解釋:按照計算機中有符號數的存儲規則, '-9' 的補碼按照不同位數表示是 [10111] 、[110111]、[1110111]...但是我們已經假設了機器字長為4位,所以必須丟棄超過限定字長的部分,丟棄后得到的補碼就是 [0111].這與第一種解釋相吻合。 

  這樣我們就能得出一個結論:即使加法產生了溢出,進行逆運算后的值依然等於參與運算的初值,即無論加法是否溢出,而(x+y)-y 總是會求值得到 x

  好了,我們已經利用這個簡單的例子說明這個運算時可逆的,也就證明了上面紅體字部分的正確性,也就說明給出的解決辦法是錯誤的。

正確的解決辦法:

1 int tadd_ok(int x,int y){
2     int sum = x+y;
3     int neg_over = x < 0 && y < 0 && sum >= 0;
4     int pos_over = x >=0 && y >= 0 && sum < 0;
5     return !neg_over && !pos_over;
6 }

  后記:數據的溢出往往會造成程序的不可靠性,所以在程序中應該考慮到這個問題所可能產生的后果,並采取必要的措施來處理這種情況。當然,這樣做的前提是你已經對溢出有了比較深入的理解。

  

  注:本文整理自《深入理解計算機系統》.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM