long a,b;
cin>>a>>b;
long i;
i = a+b;
if((i^a)<0 && (i^b)<0)
cout<<"溢出";
我們來分析一下,為什么這樣可以,
分情況討論:
i^a<0 &&i^b<0
如果a是正數
要使得溢出,b肯定是正數,這時候得出c是負數,那說明最高位溢出了。
如果a是負數,b肯定是負數才能溢出,所以,相加得到了正數,說明溢出。
算了,還是看別人的吧。哈哈哈哈哈哈哈
如何檢測整型相加溢出(overflow)
前言:
本文主要討論如何判斷整型相加溢出(overflow)的問題. 我們知道計算機里面整型一般是有限個字節(4 bytes for int)表示, 正是因為只能用有限個字節表示一個整型變量, 由此帶來一個可能的問題: 溢出(overflow). 所謂整型溢出(overflow), 是說一個整數的值太大或者太小導致沒有用給定的有限個(比如四個字節沒法存超過2^31 – 1的有符號正整數)字節存儲表示. 這個整型溢出(overflow)問題一般的時候不會注意到也並不危險, 但是在做整型加法或者乘法的時候就有可能出現並且給程序帶來未定義的行為. 這里我們主要討論如何判斷整型相加溢出(overflow)的兩種方法以及各自優缺點.
整型相加溢出(overflow)的原因:
前言里面也已經提到了, 計算機中的的整數是用有限個字節表示的, 假設用k個字節表示一個整型變量, 那么這個變量可以表示的有符號整數的范圍是-2^(8k-1) ~ 2^(8k-1) – 1 , 那么兩個正整數或者兩個負整數相加就有可能超過這個整型變量所能表示的范圍, 向上超出>2^(8k-1) – 1我們稱之為向上溢出, 向下超出<-2^(8k-1), 我們稱之為向下溢出. 注意這里兩個整數符號相同是整型相加溢出(overflow)的必要條件, 也就是說只有符號相同的兩個整數相加才有可能產生溢出(overflow)問題. 這個可以這么理解: 你想要是兩個符號不同的兩個整數, 他們相加, 那么這個和的值的絕對值一定是比單個相加數和被相加數都小, 既然相加數和被相加數都能用現有整型變量表示, 那么兩個不同整數的相加結果怎么樣都可以用現有的整型范圍的變量存儲下來而不溢出(overflow). 所以結論: 只有符號相同的整數相加才有可能才生溢出(overflow).
整型相加溢出(overflow)的檢測:
那么接下來的問題就是如何檢測到溢出(overflow)的產生的, 更具體的, 給定兩個整型, 比如int a, int b, 我們做加法a+b, 如何去判斷這個相加的結果是正確結果還是說是溢出(overflow)的結果. 下面我們給出兩種方法(后面我們會討論方法二是更好的方法), 並且做出解釋或者說不太嚴格的證明方法的正確性, 也就是為什么這么做就能保證溢出(overflow)的檢測的正確性.
方法一, 計算相加的結果, 判斷結果的符號, 兩個正整數相加結果為負數, 或者兩個負整數相加結果為正數, 那么就是溢出(overflow)了. 實現代碼如下:
int addInt(int a, int b) { int res = a + b; if(a > 0 && b > 0 && res < 0) throw overflow_exception; if(a < 0 && b < 0 && res > 0) throw overflow_exception; return res; }
這個方法的原理是這樣, 計算機里面有符號整數是利用補碼的形式表示的, 第一位是符號位, 0表示整數, 1表示負數. 我們拿一個字節的整型來舉例, 一個字節的有符號數可以表示的范圍就是-128 ~ 127, 那么兩個一個字節的正整數相加的最大范圍就是254, 那么其中128 ~ 254就是溢出(overflow)的值, 是不能用一個字節存儲下的值, 這個值用一個字節表示的時候最高位是1, 在有符號整數系統里面這個值其實被當成了負數. 同理, 負數相加的時候最小可以到達-256, 根據補碼的表示對應的正整數取反加1就是對應的補碼, 那么對應的正整數的最高位是1, 現在取反以后就變成0, 也就是說兩個比較大的負數相加的結果其實變成了正數. 這就是上述方法的理論基礎.
方法二, 使用減法, 利用現有整型的最大或者最小極值減去某個加數(減法相當於變號, 從而保證沒有溢出(overflow)的發生), 和另一個加數比較大小進行判斷. 實現代碼如下:
int addInt(int a, int b) { if(a > 0 && b > 0 && a > INT_MAX - b) throw overflow_exception; if(a < 0 && b < 0 && a < INT_MIN - b) throw overflow_exception; return a + b; }
這個方法其實不用太多的解釋, 簡單的數學知識就能解釋其中的原理, 由於減法保證了不會溢出(overflow), 又前面我們保證了兩個數都是正整數, 所以形如 a > INT_MAX – b的判斷是安全並且總是正確的. 而且這個檢測方法的正確性可以通過移位就看得懂了, 不像方法一, 需要一定的計算機底層的知識才能解釋說通.
方法一和方法二比較: 我自己一開始的時候思考利用方法一這樣的結論去判斷溢出(overflow), 但是我心里其實不放心, 因為方法一的前提是”兩個正整數相加溢出的充要條件是符號位變成1, 也就是結果變成了負數”, 這樣的結論或者事實對於我或者一般人來講其實並不是那么的直觀或者理所當然, 當然了我自己又用那個一個byte的例子試着去解釋, 結論還是正確的, 所以方法一相比較方法二而言並不直觀. 另一方面有說法說是溢出的時候結果其實不確定, 上面在方法一里面我們的分析只是理論上的分析, 編譯器有可能做出相關的優化或者對溢出結果做出調整, 那么可能就出現未定義的行為了, 所以綜上所述, 方法二應該是比較更為安全和合理並且更為直觀的首選檢測整數相加溢出(overflow)的方法.
更新: 感謝網友Stanley的留言, 提供了第三種方法的檢測, 其實也就是方法一的bit operation版本, 通過位操作, 我們可以判斷求和結果x是否與a和b還同號, 如果同時不同號(也就是sign bit不相同了), 那我們就相當於檢測到了溢出. Stanley的版本稍微反了反, 我認為應該是下面這種情況才是溢出, 如有錯誤, 敬請指正. Thanks!
x = a + b; if ((x^a) < 0 && (x^b) < 0) { //overflow, do something }
結束語:
本文主要討論了如何判斷整型相加溢出(overflow)的問題, 主要總結了整型相加溢出(overflow)的原因, 並給出了兩種檢測整型相加溢出(overflow)的方法, 方法一基於計算結果的正負, 方法二基於把加法轉化為減法. 本文同時給出了兩種方法的比較, 並且指出方法二應該是首選的檢測方法.