2010-11-29 255 views
16

我有下面的代碼:爲什麼-1 >> 1是-1?而1 >> 1是0!

std::cout << (-10 >> 1) << std::endl; 
std::cout << (-9 >> 1) << std::endl; 
std::cout << (-8 >> 1) << std::endl; 
std::cout << (-7 >> 1) << std::endl; 
std::cout << (-6 >> 1) << std::endl; 
std::cout << (-5 >> 1) << std::endl; 
std::cout << (-4 >> 1) << std::endl; 
std::cout << (-3 >> 1) << std::endl; 
std::cout << (-2 >> 1) << std::endl; 
std::cout << (-1 >> 1) << std::endl; 

結果是:

-5 
-5 
-4 
-4 
-3 
-3 
-2 
-2 
-1 
-1 

但是,爲什麼?

-11111 1111(1字節),-1 >> 1必須是:1011 1111,這是不是-10! (我知道標誌位沒有移位)

有人能告訴我這是如何工作的嗎?

+0

每一位都被移位,並且在二進制補碼中沒有明確的符號位。 – jalf 2010-11-30 01:24:38

回答

24

標準5.8/3(移位運算符):

E1 E2 >>的值是E1 右移E2比特位置。如果E1 具有一個無符號類型,或者E1具有 符號類型和具有非負值,所述 結果的值是E1 的商通過升高至 電源E2的數量除以2的 組成部分。 如果E1的簽名類型爲 且結果爲負值,則結果值 的值是實現定義的。

所以對於「爲什麼?」這個問題,標準答案是:爲什麼不呢。

+8

標準答案是:「RTFM for your compiler」 – 2010-11-29 22:00:22

17

將負數右移是實現定義的。

將符號擴展位移入最左邊位的實現按照您的報告工作。

至於爲什麼做這樣,那是因爲向右移位,可以使用2的冪與舍入,向負無窮大(例如像floor())舍入行爲來劃分:

(-8 >> 2) == -2 
(-9 >> 2) == -3 
(-10 >> 2) == -3 
(-11 >> 2) == -3 
(-12 >> 2) == -3 

See this SO question.

3

-1 >> 1必須是:1011 1111

如果這是真的,-10 >> 1。將10111011 == -69的補碼。不是一個非常有用的結果!

雖然語言行爲未定義(所以結果,有用或其他不能依賴的結果),但是行爲(以及在這種情況下表現出來的行爲)是執行「符號擴展」。

這是不正確的「籤位不發生移位」,它移位,騰出的空位充滿等於符號位的值。這種行爲既保留了符號,又提供了除了-1之外的所有值所觀察到的'預期'除2運算。對於負值,-1是右移的'終值',因爲0是正值。也就是說,負數的右移趨於-1,正數趨於零。

3

通常,右移被定義爲實現爲「算術」或「邏輯」。區別在於,對於邏輯右移,最左邊的位始終設置爲零。通過算術右移,最左邊的位是前一個值的副本。

例如,讓我們假裝該值僅爲8位,以便更容易跟蹤。然後:

邏輯:

0111 1111 >> 1 = 0011 1111 
1111 1111 >> 1 = 0111 1111 
1111 1110 >> 1 = 0111 1111 

算術:

0111 1111 >> 1 = 0011 1111 
1111 1111 >> 1 = 1111 1111 
1111 1110 >> 1 = 1111 1111 

算術右移等效於除以2並朝向負無窮舍入。

在C++中,右移運算符是邏輯還是算術是特定於實現的。即每個編譯器編寫者可以自己決定,可能是基於給他正在工作的計算機的體系結構提供什麼更容易的做法。

您聲明符號位未被移位是不正確的。符號位像所有其他位一樣被移位。唯一的問題是什麼取代它。

Java有兩種不同的右移運算符:>>是算術而>>>是邏輯的。

+0

@laalto:哦,謝謝編輯。當我輸入時,它在我的屏幕上可讀。:-( – Jay 2010-11-30 16:06:49

相關問題