1. 程式人生 > 其它 >移位運算 and 原碼、反碼、補碼

移位運算 and 原碼、反碼、補碼

文章目錄

背景

在看《Absolute C++》時遇到的。判斷以下程式碼的輸出內容:

int i=1;
while (i<<=10)
{
    cout << i << endl;
    i += 3;
}

上面while判別式中i<<=10可不是小於或遠小於的大小關係比較,而是移位運算的表示式,相當於i = i << 10。那麼結果如何呢?

  • C++中賦值是一種表示式,該表示式的返回值為所賦的值
  • 那也就是看上面的程式碼中,“先左移10位,再加十進位制3”這一對操作,會不會有某一次使左移之後的值為0

什麼是移位運算,如何做

各種變數在計算機中都是以二進位制的形式存在,比如十進位制1就是10,移位運算就是直接操作該變數的二進位制數,二進位制又有“原碼、反碼、補碼”(看這篇筆記二進位制:原碼反碼與補碼),移位運算的操作物件是補碼

移位的分類,根據移位方向不同,分為:

  • 左移位<<:符號位不參與移動,把二進位制補碼整體向左移動多少位
    • 無論正數還是負數,向左移動之後右邊空出來的位,用0補
  • 右移位>>:符號位不參與移動,二進位制補碼向右移動多少位
    • 向右移動之後左邊與符號位之間空出來的位,用符號位補
      • 正數的話,符號位為0,用0補
      • 負數的話,符號位為1,用1補

int型變數在64位機器中佔多少個二進位制位

其實這一塊主要是答疑:十進位制1的二進位制10,那左移2位之後為啥不是二進位制00、十進位制0,而是變成了十進位制4.

image-20210330094809428.png

其實答案就是,十進位制1的二進位制10,並不是只佔兩個二進位制位,在10兩位的左側,還有很多個二進位制位。多少個呢:

int型變數一般長度為4個位元組,每個位元組為8個二進位制位,那麼總長也就是4*8=32位。

所以只要左移不超過32-2=30位,就不會移出被丟棄。

移位前後數值的大小關係

從二進位制到十進位制的轉換可以瞭解其關係:

二進位制:0 0 1 1 0 1 0
十進位制:$ 2^4 + 2^3 + 2^1$

如果左移1位:

二進位制:0 1 1 0 1 0 0
十進位制:$ 2^5+2^4+2^2=2*(2^4 + 2^3 + 2^1)$

結論:

  • 左移的時候,如果沒有移出左邊界,則 結 果 = 原 數 ∗ 2 移 動 的 位 數 結果 = 原數 * 2^{移動的位數} =2

    • 怎麼判斷有沒有移出呢?該數原來的二進位制數最左側的1在第幾位 and 該型別的所佔位數
  • 右移的時候,如果沒有移出左邊界,則 結 果 = 原 數 / 2 移 動 的 位 數 結果 = 原數 / 2^{移動的位數} =/2

回到文章開頭的題目

int i=1;
while (i<<=10)
{
    cout << i << endl;
    i += 3;
}

十進位制1與十進位制3的原碼與補碼:

            原  碼                       補  碼
1   0000 0000 ... 0000 0001     0111 1111 ... 1111 1111
3   0000 0000 ... 0000 0011     0111 1111 ... 1111 1101
    -----------------------     -----------------------
            共32位                       共32位
0   0000 0000 ... 0000 0000     0000 0000 ... 0000 0000

注意int型,32位,可表示範圍為[2^32/2, 2^32/2=214 748 364 8)

首先,對補碼進行左移位操作,每次左移10位,移位之後右邊的空位補0;

其次,while判別式中實際上是一個表示式 i = i << 10

那也就是判斷,每次左移10位再加十進位制3,會不會有一次遇到i=0的情況:

  • 第一次判斷時, i = 1 ∗ 2 10 = 1024 i=1*2^{10}=1024 i=1210=1024,非0,進入while
    • +3, i = 2 10 + 3 = 1027 i=2^{10}+3=1027 i=210+3=1027
  • 第二次判斷時, i = 1027 ∗ 2 10 = 1051648 i=1027*2^{10}=1051648 i=1027210=1051648,非0,進入while
    • +3, i = 1051648 + 3 = 1051651 i=1051648+3=1051651 i=1051648+3=1051651
  • 第三次判斷時, i = 1051651 ∗ 2 10 = 1076890624 i=1051651*2^{10}=1076890624 i=1051651210=1076890624,非0, 且沒有超出右邊界,進入while
    • +3, i = 1076890624 + 3 = 1027890627 i=1076890624+3=1027890627 i=1076890624+3=1027890627
  • 第4次及之後判斷時,$i_{n-1}*1024 > 右邊界,輸出結果不確定性

這樣二進位制與十進位制轉換,太麻煩了,如果只是判斷while迴圈會不會停止的話,可以僅分析二進位制。0的二進位制各位均為0,只要看某次+3(二進位制再左移10位後是否各位均為0即可:

  • 十進位制1的二進位制,僅最右側有1個1,左移10位後,右邊10位均為0;
  • 加十進位制3後,看上面3的補碼,右邊10位有9個1,所相加之後右10位與3的補碼相同,含有1
  • 進入while判別式,再左移10位,然後再加3,右邊的9個1一直無法移出去,也就是說始終無法達到全0,即十進位制0,所以while無限迴圈。