關於補零擴展與補符號位擴展

衆所周知,每種基本數據類型都有一個固定的位數,比如byte佔8位,short佔16位,int佔32位等。正因如此,當把一個低精度的數據類型轉成一個高精度的數據類型時,必然會涉及到如何擴展位數的問題。這裏有兩種解決方案:
(1)補零擴展:填充一定位數的0。
(2)補符號位擴展:填充一定位數的符號位(非負數填充0,負數填充1)。
對於無符號類型(相當於都是非負數)與有符號類型中的非負數部分,這兩種方法沒有區別,都是填充0;對於有符號類型中的負數部分,這兩種方法就會產生差異了,補零擴展會填充0,而補符號位擴展會填充1。下面將byte類型的-127轉爲int類型爲例,探討一下這兩種方法的區別。
首先必須明確一些知識點:

  • 計算機是用補碼來存儲數字的;
  • 正數的補碼等於原碼;
  • 負數的補碼等於反碼+1;
  • 一個數的補碼的補碼等於原碼。

127原碼1111 1111,反碼1000 0000,補碼1000 0001。計算機存儲的是1000 0001,用十六進制表示爲0x81。
當使用補零擴展時,結果爲:
0000 0000 0000 0000 0000 0000 1000 0001
用十六進制表示爲0x81。爲了計算十進制值,計算它的補碼,結果爲:
0000 0000 0000 0000 0000 0000 1000 0001
將這個二進制數轉成十進制的結果是129。
當使用補符號位擴展時,結果爲:
1111 1111 1111 1111 1111 1111 1000 0001
用十六進制表示爲0xFFFFFF81。爲了計算十進制值,計算它的補碼,結果爲:
1000 0000 0000 0000 0000 0000 0111 1111
將這個二進制數轉成十進制的結果是-127。
由此可以得出結論:
(1)使用補零擴展能夠保證二進制存儲的一致性,但不能保證十進制值不變。
(2)使用補符號位擴展能夠保證十進制值不變,但不能保證二進制存儲的一致性。
下面以Java爲例進行驗證(Java中沒有無符號類型,只有有符號類型):

public static void main(String[] args){
    byte b = -127;
    System.out.println(Integer.toBinaryString((int)b));
    System.out.println(Integer.toBinaryString((int)(b & 0xFF)));
}

輸出:
11111111111111111111111110000001
10000001

由此又可以得出結論:
(1)對於有符號類型,Java使用的是補符號位擴展,這樣能保證十進制的值不變;
(2)如果想要用補零擴展來確保二進制的內容不變,可以通過& 0xFF(不一定是8位,根據待轉換的數據位數而定)實現。本質是將前面補上的1都通過與運算改成了0。
關於b & 0xFF爲什麼會導致結果改變的問題,個人認爲應當是在進行這個運算之前,b已經進行了補符號位擴展,變成了32位(否則 b & 0xFF → 1000 0001 & 1111 1111 == 1000 0001 == b)。

發佈了42 篇原創文章 · 獲贊 28 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章