為什麼0取反再右移任意位得出數總是-1?Milo Yip2017-10-29 14:48:29

對有符號的整數型別(如 int)進行右移,在標準中是由平臺自行定義的,不過一般是使用二補數表示有符號整數,而右移是算術右移(arithmetic right shift),即是會用原來的最高位(MSB)來填補右移後左面的空位。

為什麼0取反再右移任意位得出數總是-1?

為什麼0取反再右移任意位得出數總是-1?

字面量 0 的型別為 int,取反後全部位為 1,作為有符號整數是 -1,算術右移後也不變。

如果用字面量 0u,結果就不一樣了。

圖源:

https://

en。m。wikipedia。org/wiki

/Arithmetic_shift#/media/File%3ARotate_right_arithmetically。svg

為什麼0取反再右移任意位得出數總是-1?Hael C2017-10-29 15:27:41

最近正好在看CSAPP(Computer System: A Programmer‘s Perspective,中文譯本為深入理解計算機系統),就來回答一下叭w~

首先簡單介紹一下機器中整數(int)的儲存形式:絕大多數情況下,不管是32位機器還是64位機器,int都是4個位元組(32位)。其中最高位(MSB, the Most Significant Bit)

x_{31}

視作符號位,符號位為0則為非負數,符號位為1則為負數。

用向量的形式表示計算機中字元的儲存,則可以表示為

[x_{31},x_{30},x_{29},...x_{2},x_1,x_0]

,對應表示的整數為

-x_{31}\cdot2^{31}+\sum_{i=0}^{30}{x_i\cdot2^i}

。所以,int可以表示的最大數

T_{Max}=[0111...111]=2^{31}-1

,最小數

T_{Min}=[1000...0000]=-2^{31}

0作為整數(Integer),在機器中的表示形式為000。。。。000(32個0),取反後為1111。。。1111。根據上述計算公式可得二進位制碼1111。。。1111即代表整數-1。

那麼為什麼右移任意位依然是-1呢?

右移有兩種形式:邏輯右移(logical right shift)和算數右移(arithmetic right shift)。

邏輯右移是將左邊空位補0。所以將int右移k位便得到

[0,...,0,x_{31},x_{30},...,x_k]

算數右移是將左邊空位補上MSB位。所以將int右移k位便得到

[x_{31},...x_{31},x_{31},x_{30},...x_k]

。通俗的講法即非負數右移補0,負數右移補1。

C標準並沒有規定應該使用哪一種右移方式,不過絕大部分情況下使用的是算數右移。而1111。。。1111右移任意位依然是1111。。。1111,所以結果總是-1。

為什麼0取反再右移任意位得出數總是-1?冒泡2017-10-30 03:37:25

沒記錯的話C語言中右移一個負數,左邊補0還是1是implementation defined,不過就算從算數角度說,0取反右移也是1啊:

不考慮實際條件的限制,0等於是……0000000000(無窮個0),取反後是……111111111111(無窮個1),無窮個1右移還是無窮個1,也就是-1嘛

為啥無窮個1是-1呢,計算0-1,不夠減,往前借位,還不夠借,無限借下去,想象一下最後“假裝”借到了,就變成無窮1了,或者無窮個1加一,無限進位過去,就是0了

注:差不多是這個道理,但上面的某些“證明”別當真,哈哈

為什麼0取反再右移任意位得出數總是-1?知乎使用者2017-10-30 10:46:52

你如果是按位取反,~0就變成了111111。。。1,右移一般補符號位,也就是還是111。。。1,也就是補碼的-1了。

如果你是邏輯取反,!0就是1,右移就是0了。

為什麼0取反再右移任意位得出數總是-1?myqTmac2017-10-31 13:25:22

那得看型別吧,signed型別是算數右移,補最高位,unsigned是邏輯右移補0