1. 程式人生 > >java中位運算

java中位運算

最近又回去重新看了java基礎的書籍,在記錄總結以前一些比較容易混淆的知識點。下面是本篇要記錄的內容

 

一、 相關基礎概念

在開始java位運算的知識之前,我們先來了解幾個基礎的概念,機器數,真值,原碼,反碼,補碼。

1.機器數

我們知道無論是程式碼還是數值,在計算機中最後都轉換成以二進位制的形式存在的,而一個數值在計算機中的二進位制表示形式,就是這個數的機器數。機器數是有符號位的,在計算機中用一個二進位制數的最高位存放符號,正數為0,負數為1,如下例項(按原碼錶示):

十進位制的+5,計算機字長為8位,其二進位制就是00000101

十進位制的 -5,計算機字長為8位,其二進位制就是10000101(這裡用的是原碼)

其中00000101和10000101就是機器數

2.真值

由於機器數的第一位是符號位,所以其形式值就不等於其真值的數值,也就是說10000101表示的是-5而不是133(10000101的十進位制是131,前提是不算最高位為符號位),因此-5才是機器數的真值。

3.原碼

 

    原碼是一種計算機中對數字的二進位制定點表示方法。原碼錶示法在數值前面增加了一位符號位(即最高位為符號位):正數該位為0,負數該位為1,其餘位表示數值的大小。

[+5]=[00000101](原碼)

[ -  5]=[10000101](原碼)

因為第一位是符號位,因此8位二進位制的取值範圍就是[1111 1111,0111 1111]也就是[-127,127]

4.反碼

反碼是數值儲存的一種,但是由於補碼更能有效表現數字在計算機中的形式,所以多數計算機一般都不採用反碼錶示數,反碼的表示方法如下:

  • 正數的反碼是其本身

  • 負數的反碼是在其原碼的基礎上, 符號位不變,其餘各個位取反.

[+5]=[00000101](原碼)= [00000101](反碼)

[ -  5]=[10000101](原碼)= [11111010](反碼)


5.補碼

在計算機系統中,數值一律用補碼來表示和儲存。原因在於,使用補碼,可以將符號位和數值域統一處理;同時,加法和減法也可以統一處理。補碼的表示方法是:

  • 正數的補碼就是其本身

  • 負數的補碼是在其原碼的基礎上, 符號位不變, 其餘各位取反, 最後+1. (即在反碼的基礎上+1)

[+5]=[00000101](原碼)= [00000101](反碼)=[00000101](補碼)

[ -  5]=[10000101](原碼)= [11111010](反碼)=[11111011](補碼)


6.小結

計算機中的符號數有三種表示方法,即原碼、反碼和補碼。三種表示方法均有符號位和數值位兩部分,符號位都是用0表示“正”,用1表示“負”,而數值位,三種表示方法各不相同。而在計算機系統中,數值一律用補碼來表示和儲存。

二、 Java位運算

位移操作:(只針對 int型別的資料有效,java中,一個int的長度始終是32位,也就是4個位元組,它操作的都是該整數的二進位制數).也可作用於以下型別,即 byte,short,char,long(它們都是整數形式)。當為這四種類型時,JVM先把它們轉換成int型再進行操作。

7.左移(<<)

m<<n的含義:把整數m表示的二進位制數左移n位,高位移出n位都捨棄,低位補0.  (此時將會出現正數變成負數的可能),如下例項:

5<<2 :把十進位制的數值5左移兩位,按如下步驟計算,

  • 把5轉位16位的二進位制機器數:00000000 00000000 00000000 00000101

  • 按左移原理,將二進位制數左移兩位:00000000 00000000 00000000 00010100

  • 左移後結果為20

5<<29:把十進位制的數值5左移29位,按如下步驟計算,

 

  • 把5轉位16位的二進位制機器數:00000000 00000000 00000000 00000101

 

  • 按左移原理,將二進位制數左移29位:10100000 00000000 00000000  00000000

 

  • 左移後高位是1,結果顯然是負數

小結:m<<n即在數字沒有溢位的前提下,對於正數和負數,左移n位都相當於m乘以2的n次方.

8.右移(>>)

 m>>n的含義:把整數m表示的二進位制數右移n位,m為正數,高位全部補0;m為負數,高位全部補1,例項如下:

 

5>>2 :把十進位制的數值5右移兩位,按如下步驟計算,

  • 把5轉位16位的二進位制機器數:00000000 00000000 00000000 00000101

  • 按右移原理,將二進位制數左移兩位:00000000 00000000 00000000 00000001

  • 右移後結果為1

-5>>2:把十進位制的數值-5右移兩位,按如下步驟計算,

  • 把-5轉位16位的二進位制機器數:11111111 11111111 11111111 11111011

  • 按右移原理,將二進位制數右移兩位:11111111 11111111 11111111 11111110

  • 右移後結果為-2

小結:

 m>>n即相當於m除以2的n次方,得到的為整數時,即為結果。如果結果為小數,此時會出現兩種情況:

  • 如果m為正數,得到的商會無條件 的捨棄小數位;

  • 如果m為負數,捨棄小數部分,然後把整數部分加+1得到位移後的值。

9.無符號右移(>>>)

m>>>n:整數m表示的二進位制右移n位,不論正負數,高位都補0,例項如下:

 

5>>>2 :把十進位制的數值5右移兩位,按如下步驟計算,

  • 把5轉位16位的二進位制機器數:00000000 00000000 00000000 00000101

  • 按右移原理,將二進位制數左移兩位:00000000 00000000 00000000 00000001

  • 右移後結果為1

-5>>>2:把十進位制的數值-5右移兩位,按如下步驟計算,

  • 把-5轉位16位的二進位制機器數:11111111 11111111 11111111 11111011

  • 按右移原理,將二進位制數右移兩位:00111111 11111111 11111111 11111110

  • 右移後結果為正數

 

10.按位非操作(~)

~ 按位取反操作符,對每個二進位制位的內容求反,即1變成0,0變成1例項如下

  • 把-5轉位16位的二進位制機器數:11111111 11111111 11111111 11111011

  • ~(-5) 取反結果:00000000 00000000 00000000 00000100 

  • 轉為十進位制,結果為4

11.按位與操作(&)

& 位與操作符,對應的二進位制位進行與操作,兩個都為1才為1,其他情況均為0,原理如下:

  • 1&0=0

  • 0&0=0

  • 1&1=1

  • 0&1=0

例項:-5 & 4

-5的二進位制形式為: 11111111 11111111 11111111 11111011

 4的二進位制形式為:  00000000 00000000 00000000 00000100

——————————————————————————————

邏輯與運算結果:     00000000 00000000 00000000 00000000

最終結果為0。

12.按位或操作(|)

| 位或操作符,對應的二進位制位進行或操作,兩個都為0才為0,其他情況均為1,原理如下:

  • 1|0=1

  • 0|0=0

  • 1|1=1

  • 0|1=1

例項:-5 | 4

 -5的二進位制形式為:11111111 11111111 11111111 11111011

  4的二進位制形式為:00000000 00000000 00000000 00000100

————————————————————————————

邏輯或運算結果:    11111111 11111111 11111111 11111111

最終結果為-1。

利用或的原理我們可以把位元組轉換為整數,-64&0xFF=192,其中0xFF表示整數255。

 

13.按位異或操作( ^ )

^ 異或操作符,相同位值為0 否則為1,原理如下:

  • 1^1=0

  • 1^0=1

  • 0^1=1

  • 0^0=0

 

例項:-5 ^  4

 -5的二進位制形式為:11111111 11111111 11111111 11111011

  4的二進位制形式為:00000000 00000000 00000000 00000100

————————————————————————————

邏輯異或運算結果:    11111111 11111111 11111111 11111111

最終結果為-1。

其實利用邏輯異或操作有個作用就是可以比較兩個數值是否相等,即利用1^1=0,0^0=0的原理,如5^5==0。

14.總結

通過上面的分析,我們對java的位運算也算有了比較全面的瞭解,那麼我們的程式通過位運算又有什麼優勢呢?其實通過位運算確實會比我們直接的程式程式碼運算會快很多,因為位運算直接運算的是計算機底層的二進位制機器操作指令,而我們的程式程式碼運算最終也是要轉成計算機可識別的二進位制操作指令才能執行,位運算可以理解為省了中間轉換的操作,處理器可以直接操作。事實是我們在某些原始碼經常能看見如下程式碼:

 
  1. <span style="font-family:Microsoft YaHei;"> public static final int OP_CONNECT = 1 << 3;

  2. /**

  3. * Operation-set bit for socket-accept operations. </p>

  4. *

  5. * <p> Suppose that a selection key's interest set contains

  6. * <tt>OP_ACCEPT</tt> at the start of a <a

  7. * href="Selector.html#selop">selection operation</a>. If the selector

  8. * detects that the corresponding server-socket channel is ready to accept

  9. * another connection, or has an error pending, then it will add

  10. * <tt>OP_ACCEPT</tt> to the key's ready set and add the key to its

  11. * selected-key set. </p>

  12. */

  13. public static final int OP_ACCEPT = 1 << 4;</span>

其實原理是一樣的,處理器能夠直接支援和處理。

 

參考內容:

http://www.cnblogs.com/dongpo888/archive/2011/07/13/2105001.html