1. 程式人生 > >byte為什麽要與0xff

byte為什麽要與0xff

完全 參與 text 基本數據 http 解讀 jvm 知識 解決方案

面對帶正負號的數,會采用符號擴展,如果原值是正數,則高位補上0;如果原值是負數,高位補1。
二進制是計算技術中廣泛采用的一種數制。二進制數據是用0和1兩個數碼來表示的數。
當前的計算機系統使用的基本上是二進制系統,數據在計算機中主要是以補碼的形式存儲的。
計算機中的二進制則是一個非常微小的開關,用“開”來表示1,“關”來表示0。

我們都知道Java的基本數據類型內存中都有一個固定的位數(內存分配空間),如byte占8位,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)使用補符號位擴展能夠保證十進制值不變,但不能保證二進制存儲的一致性。




原碼反碼補碼這三個概念

對於正數(00000001)原碼來說,首位表示符號位,反碼 補碼都是本身

對於負數(100000001)原碼來說,反碼是對原碼除了符號位之外作取反運算即(111111110),補碼是對反碼作+1運算即(111111111)

概念就這麽簡單。

當將-127賦值給a[0]時候,a[0]作為一個byte類型,其計算機存儲的補碼是10000001(8位)。

將a[0] 作為int類型向控制臺輸出的時候,jvm作了一個補位的處理,因為int類型是32位所以補位後的補碼就是1111111111111111111111111 10000001(32位),這個32位二進制補碼表示的也是-127.

發現沒有,雖然byte->int計算機背後存儲的二進制補碼由10000001(8位)轉化成了1111111111111111111111111 10000001(32位)很顯然這兩個補碼表示的十進制數字依然是相同的。

但是我做byte->int的轉化 所有時候都只是為了保持 十進制的一致性嗎?

不一定吧?好比我們拿到的文件流轉成byte數組,難道我們關心的是byte數組的十進制的值是多少嗎?我們關心的是其背後二進制存儲的補碼吧

所以大家應該能猜到為什麽byte類型的數字要&0xff再賦值給int類型,其本質原因就是想保持二進制補碼的一致性。

當byte要轉化為int的時候,高的24位必然會補1,這樣,其二進制補碼其實已經不一致了,&0xff可以將高的24位置為0,低8位保持原樣。這樣做的目的就是為了保證二進制數據的一致性。

當然拉,保證了二進制數據性的同時,如果二進制被當作byte和int來解讀,其10進制的值必然是不同的,因為符號位位置已經發生了變化。

int c = a[0]&0xff;

a[0]&0xff=1111111111111111111111111 10000001&11111111=000000000000000000000000 10000001 ,這個值算一下就是129,

所以c的輸出的值就是129。

有人問

為什麽上面的式子中a[0]不是8位而是32位,因為當系統檢測到byte可能會轉化成int

或者說byte經過一些運算後會轉化成int時,就會將byte的內存空間高位補1擴充到32位,再參與運算。

其實是從數字類型擴展到較寬的類型時,補零擴展還是補符號位擴展。
這是因為Java中只有有符號數,當byte擴展到short, int時,即正數都一樣,因為為符號位是0,所以無論如何都是補零擴展;

但負數補零擴展和按符號位擴展結果完全不同。
補符號數,原數值不變。
補零時,相當於把有符號數看成無符號數,比如-127 = 0x81,看成無符號數就是129, 256 + (- 127)
對於有符號數,從小擴展大時,需要用&0xff這樣方式來確保是按補零擴展。
而從大向小處理,符號位自動無效,所以不用處理。

也就是說在byte向int擴展的時候,自動轉型是按符號位擴展的,這樣子能保證十進制的數值不會變化

而&0xff是補0擴展的,這樣子能保證二進制存儲的一致性,但是十進制數值已經發生變化了。

也就是說按符號位擴展能保證十進制數值不變,補0擴展能保證二進制存儲不會變。

而正數可以說是既按符號位擴展,又是補0擴展,所以在二進制存儲和十進制數值上都能保證一致。

byte為什麽要與0xff