2012-11-23 43 views
0
int left = std::numeric_limits<int>::min(); 
int right = -1; 
//the code below instead of give one more than int_max gives: 18446744071562067968 
unsigned long long result = left * right; 

我試圖查找UAC,但即使根據UAC規則,這應該會產生正確的輸出。任何想法爲什麼結果不正確?整數轉換問題

回答

4

這是未定義的行爲,因爲結果超出了該類型的範圍,因此將帶符號2的補碼int的最小值乘以-1。

在這種情況下,您的輸出與-2147483648的結果一致,即溢出似乎已經纏繞。你不能依賴環繞簽名類型,只有無符號類型。

將計算結果分配給unsigned long long不會改變執行計算的類型。只要您執行乘法,就會丟失。因此,在之前將其中一個操作數轉換爲unsigned long long

+0

#Steve Jessop,不應該導致溢出?就像max_int?有沒有解釋這種行爲的規則? – smallB

+0

@smallB:有符號溢出在C++中是未定義的行爲,它*不保證環繞。它經常會環繞(這與您看到的結果一致),但是像gcc這樣的常見編譯器包含優化,如果它包含您假定將會環繞的溢出,則可能會破壞您的代碼,但編譯器假定不會發生,因爲它們是禁止。 5/4,「如果在評估一個表達式時,結果不是數學上定義的,或者不在其類型的可表示值範圍內,則行爲是未定義的。」 –

4

兩個操作數都是int,所以算術在int類型內執行;該操作的結果溢出int的範圍,所以結果未定義。

爲了得到你所期望的結果,投一個操作數long long第一:

unsigned long long result = left * (long long) right; 

這仍然是潛在的不確定的行爲;它的安全轉換爲無符號運算儘早(因爲無符號運算包裹和不溢出):

unsigned long long result = left * (unsigned long long) right; 

請注意,您在抵達結果是0xffffffff80000000;這表明該操作的實際結果爲int類型中的std::numeric_limits<int>::min(),該類型隨後被簽名擴展並轉換爲unsigned long long

+0

@smallB:你是怎麼想到?嘗試以上,看看是否你想要的。我認爲史蒂夫傑索普上面的回答更爲正確。 – Zane

0

Tha的原因是該乘法以int提交。

兩個參數都是int,所以乘法給出了一個int agein,你是正確的,它給int_max + 1,並equivivalent到int_min = -2147483648。因此,它實際上是-2147483648,但對於無符號長很長是equivivalent到18446744071562067968,看到的十六進制代碼:

     int_min =   80000000 
(unsigned long long) (int min) = ffffffff80000000