天天看點

java移位操作

----

http://jinguo.iteye.com/blog/540150

移位運算符就是在二進制的基礎上對數字進行平移。按照平移的方向和填充數字的規則分為三種:<<(左移)、>>(帶符号右移)和>>>(無符号右移)。

  在移位運算時,byte、short和char類型移位後的結果會變成int類型,對于byte、short、char和int進行移位時,規定實際移動的次數是移動次數和32的餘數,也就是移位33次和移位1次得到的結果相同。移動long型的數值時,規定實際移動的次數是移動次數和64的餘數,也就是移動66次和移動2次得到的結果相同。

  三種移位運算符的移動規則和使用如下所示:

  <<運算規則:按二進制形式把所有的數字向左移動對應的位數,高位移出(舍棄),低位的空位補零。

  文法格式:

  需要移位的數字 << 移位的次數

  例如: 3 << 2,則是将數字3左移2位

  計算過程:

  3 << 2

  首先把3轉換為二進制數字0000 0000 0000 0000 0000 0000 0000 0011,然後把該數字高位(左側)的兩個零移出,其他的數字都朝左平移2位,最後在低位(右側)的兩個空位補零。則得到的最終結果是0000 0000 0000 0000 0000 0000 0000 1100,則轉換為十進制是12.數學意義:

  在數字沒有溢出的前提下,對于正數和負數,左移一位都相當于乘以2的1次方,左移n位就相當于乘以2的n次方。

  >>運算規則:按二進制形式把所有的數字向右移動對應巍峨位數,低位移出(舍棄),高位的空位補符号位,即正數補零,負數補1.

  文法格式:

  需要移位的數字 >> 移位的次數

  例如11 >> 2,則是将數字11右移2位

  計算過程:11的二進制形式為:0000 0000 0000 0000 0000 0000 0000 1011,然後把低位的最後兩個數字移出,因為該數字是正數,是以在高位補零。則得到的最終結果是0000 0000 0000 0000 0000 0000 0000 0010.轉換為十進制是3.數學意義:右移一位相當于除2,右移n位相當于除以2的n次方。

  >>>運算規則:按二進制形式把所有的數字向右移動對應巍峨位數,低位移出(舍棄),高位的空位補零。對于正數來說和帶符号右移相同,對于負數來說不同。

  其他結構和>>相似。

  小結

  二進制運算符,包括位運算符和移位運算符,使程式員可以在二進制基礎上操作數字,可以更有效的進行運算,并且可以以二進制的形式存儲和轉換資料,是實作網絡協定解析以及加密等算法的基礎。

  執行個體操作:

  public class URShift {

  public static void main(String[] args) {

  int i = -1;

  i >>>= 10;

  //System.out.println(i);

  mTest();

  }

  public static void mTest(){

  //左移

  int i = 12; //二進制為:0000000000000000000000000001100

  i <<= 2; //i左移2位,把高位的兩位數字(左側開始)抛棄,低位的空位補0,二進制碼就為0000000000000000000000000110000

  System.out.println(i); //二進制110000值為48;

  System.out.println("<br>");

  //右移

  i >>=2; //i右移2為,把低位的兩個數字(右側開始)抛棄,高位整數補0,負數補1,二進制碼就為0000000000000000000000000001100

  System.out.println(i); //二進制碼為1100值為12

  System.out.println("<br>");

  //右移example

  int j = 11;//二進制碼為00000000000000000000000000001011

  j >>= 2; //右移兩位,抛棄最後兩位,整數補0,二進制碼為:00000000000000000000000000000010

  System.out.println(j); //二進制碼為10值為2

  System.out.println("<br>");

  byte k = -2; //轉為int,二進制碼為:0000000000000000000000000000010

  k >>= 2; //右移2位,抛棄最後2位,負數補1,二進制嗎為:11000000000000000000000000000

  System.out.println(j); //二進制嗎為11值為2

  }

  }

  在Thinking in Java第三章中的一段話:

  移位運算符面向的運算對象也是

  二進制的“位”。 可單獨用它們處理整數類型(主類型的一種)。左移位運算符(<<)能将運算符左邊的運算對象向左移動運算符右側指定的位數(在低位補0)。 “有符号”右移位運算符(>>)則将運算符左邊的運算對象向右移動運算符右側指定的位數。“有符号”右移位運算符使用了“符号擴充”:若值為正,則在高位插入0;若值為負,則在高位插入1。Java也添加了一種“無符号”右移位運算符(>>>),它使用了“零擴充”:無論正負,都在高位插入0。這一運算符是C或C++沒有的。

  若對char,byte或者short進行移位處理,那麼在移位進行之前,它們會自動轉換成一個int。隻有右側的5個低位才會用到。這樣可防止我們在一個int數裡移動不切實際的位數。若對一個long值進行處理,最後得到的結果也 是long。此時隻會用到右側的6個低位,防止移動超過long值裡現成的位數。但在進行“無符号”右移位時,也可能遇到一個問題。若對byte或 short值進行右移位運算,得到的可能不是正确的結果(Java 1.0和Java 1.1特别突出)。它們會自動轉換成int類型,并進行右移位。但“零擴充”不會發生,是以在那些情況下會得到-1的結果。

-----------

Java 定義的位運算(bitwise operators )直接對整數類型的位進行操作,這些整數類型包括long,int,short,char,and byte 。表4-2 列出了位運算:

表4.2 位運算符及其結果

運算符 結果

~ 按位非(NOT)(一進制運算)

& 按位與(AND)

| 按位或(OR)

^ 按位異或(XOR)

>> 右移

>>> 右移,左邊空出的位以0填充

運算符 結果

<< 左移

&= 按位與指派

|= 按位或指派

^= 按位異或指派

>>= 右移指派

>>>= 右移指派,左邊空出的位以0填充

<<= 左移指派

續表

既然位運算符在整數範圍内對位操作,是以了解這樣的操作會對一個值産生什麼效果是重要的。具體地說,知道Java 是如何存儲整數值并且如何表示負數的是有用的。是以,在繼續讨論之前,讓我們簡短概述一下這兩個話題。

所有的整數類型以二進制數字位的變化及其寬度來表示。例如,byte 型值42的二進制代碼是00101010 ,其中每個位置在此代表2的次方,在最右邊的位以20開始。向左下一個位置将是21,或2,依次向左是22,或4,然後是8,16,32等等,依此類推。是以42在其位置1,3,5的值為1(從右邊以0開始數);這樣42是21+23+25的和,也即是2+8+32 。

所有的整數類型(除了char 類型之外)都是有符号的整數。這意味着他們既能表示正數,又能表示負數。Java 使用大家知道的2的補碼(two’s complement )這種編碼來表示負數,也就是通過将與其對應的正數的二進制代碼取反(即将1變成0,将0變成1),然後對其結果加1。例如,-42就是通過将42的二進制代碼的各個位取反,即對00101010 取反得到11010101 ,然後再加1,得到11010110 ,即-42 。要對一個負數解碼,首先對其所有的位取反,然後加1。例如-42,或11010110 取反後為00101001 ,或41,然後加1,這樣就得到了42。

如果考慮到零的交叉(zero crossing )問題,你就容易了解Java (以及其他絕大多數語言)這樣用2的補碼的原因。假定byte 類型的值零用00000000 代表。它的補碼是僅僅将它的每一位取反,即生成11111111 ,它代表負零。但問題是負零在整數數學中是無效的。為了解決負零的問題,在使用2的補碼代表負數的值時,對其值加1。即負零11111111 加1後為100000000 。但這樣使1位太靠左而不适合傳回到byte 類型的值,是以人們規定,-0和0的表示方法一樣,-1的解碼為11111111 。盡管我們在這個例子使用了byte 類型的值,但同樣的基本的原則也适用于所有Java 的整數類型。

因為Java 使用2的補碼來存儲負數,并且因為Java 中的所有整數都是有符号的,這樣應用位運算符可以容易地達到意想不到的結果。例如,不管你如何打算,Java 用高位來代表負數。為避免這個讨厭的意外,請記住不管高位的順序如何,它決定一個整數的符号。

4.2.1 位邏輯運算符

位邏輯運算符有“與”(AND)、“或”(OR)、“異或(XOR )”、“非(NOT)”,分别用“&”、“|”、“^”、“~”表示,4-3 表顯示了每個位邏輯運算的結果。在繼續讨論之前,請記住位運算符應用于每個運算數内的每個單獨的位。

表4-3 位邏輯運算符的結果

A 0 1 0 1 B 0 0 1 1 A | B 0 1 1 1 A & B 0 0 0 1 A ^ B 0 1 1 0 ~A 1 0 1 0

按位非(NOT)

按位非也叫做補,一進制運算符NOT“~”是對其運算數的每一位取反。例如,數字42,它的二進制代碼為:

00101010

經過按位非運算成為

11010101

按位與(AND)

按位與運算符“&”,如果兩個運算數都是1,則結果為1。其他情況下,結果均為零。看下面的例子:

00101010 42 &00001111 15

00001010 10

按位或(OR)

按位或運算符“|”,任何一個運算數為1,則結果為1。如下面的例子所示:

00101010 42 | 00001111 15

00101111 47

按位異或(XOR)

按位異或運算符“^”,隻有在兩個比較的位不同時其結果是 1。否則,結果是零。下面的例子顯示了“^”運算符的效果。這個例子也表明了XOR 運算符的一個有用的屬性。注意第二個運算數有數字1的位,42對應二進制代碼的對應位是如何被轉換的。第二個運算數有數字0的位,第一個運算數對應位的數字不變。當對某些類型進行位運算時,你将會看到這個屬性的用處。

00101010 42 ^ 00001111 15

00100101 37

位邏輯運算符的應用

下面的例子說明了位邏輯運算符:

// Demonstrate the bitwise logical operators.

class BitLogic {

public static void main(String args[]) {

String binary[] = {"0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111", "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111"

};

int a = 3; // 0 + 2 + 1 or 0011 in binary

int b = 6; // 4 + 2 + 0 or 0110 in binary

int c = a | b;

int d = a & b;

int e = a ^ b;

int f = (~a & b) | (a & ~b);

int g = ~a & 0x0f;

System.out.println(" a = " + binary[a]);

System.out.println(" b = " + binary[b]);

System.out.println(" a|b = " + binary[c]);

System.out.println(" a&b = " + binary[d]);

System.out.println(" a^b = " + binary[e]);

System.out.println("~a&b|a&~b = " + binary[f]);

System.out.println(" ~a = " + binary[g]);

}

}

在本例中,變量a與b對應位的組合代表了二進制數所有的 4 種組合模式:0-0,0-1,1-0 ,和1-1 。“|”運算符和“&”運算符分别對變量a與b各個對應位的運算得到了變量c和變量d的值。對變量e和f的指派說明了“^”運算符的功能。字元串數組binary 代表了0到15 對應的二進制的值。在本例中,數組各元素的排列順序顯示了變量對應值的二進制代碼。數組之是以這樣構造是因為變量的值n對應的二進制代碼可以被正确的存儲在數組對應元素binary[n] 中。例如變量a的值為3,則它的二進制代碼對應地存儲在數組元素binary[3] 中。~a的值與數字0x0f (對應二進制為0000 1111 )進行按位與運算的目的是減小~a的值,保證變量g的結果小于16。是以該程式的運作結果可以用數組binary 對應的元素來表示。該程式的輸出如下:

a = 0011 b = 0110 a|b = 0111 a&b = 0010 a^b = 0101 ~a&b|a&~b = 0101 ~a = 1100

4.2.2 左移運算符

左移運算符<<使指定值的所有位都左移規定的次數。它的通用格式如下所示:

value << num

這裡,num 指定要移位值value 移動的位數。也就是,左移運算符<<使指定值的所有位都左移num位。每左移一個位,高階位都被移出(并且丢棄),并用0填充右邊。這意味着當左移的運算數是int 類型時,每移動1位它的第31位就要被移出并且丢棄;當左移的運算數是long 類型時,每移動1位它的第63位就要被移出并且丢棄。

在對byte 和short類型的值進行移位運算時,你必須小心。因為你知道Java 在對表達式求值時,将自動把這些類型擴大為 int 型,而且,表達式的值也是int 型。對byte 和short類型的值進行移位運算的結果是int 型,而且如果左移不超過31位,原來對應各位的值也不會丢棄。但是,如果你對一個負的byte 或者short類型的值進行移位運算,它被擴大為int 型後,它的符号也被擴充。這樣,整數值結果的高位就會被1填充。是以,為了得到正确的結果,你就要舍棄得到結果的高位。這樣做的最簡單辦法是将結果轉換為byte 型。下面的程式說明了這一點:

// Left shifting a byte value.

class ByteShift {

public static void main(String args[]) {

byte a = 64, b;

int i;

i = a << 2;

b = (byte) (a << 2);

System.out.println("Original value of a: " + a);

System.out.println("i and b: " + i + " " + b);

}

}

該程式産生的輸出下所示:

Original value of a: 64

i and b: 256 0

因變量a在指派表達式中,故被擴大為int 型,64(0100 0000 )被左移兩次生成值256 (10000 0000 )被賦給變量i。然而,經過左移後,變量b中惟一的1被移出,低位全部成了0,是以b的值也變成了0。

既然每次左移都可以使原來的操作數翻倍,程式員們經常使用這個辦法來進行快速的2 的乘法。但是你要小心,如果你将1移進高階位(31或63位),那麼該值将變為負值。下面的程式說明了這一點:

// Left shifting as a quick way to multiply by 2.

class MultByTwo {

public static void main(String args[]) {

int i;

int num = 0xFFFFFFE;

for(i=0; i<4; i++) {

num = num << 1;

System.out.println(num);

}

}

這裡,num 指定要移位值value 移動的位數。也就是,左移運算符<<使指定值的所有位都左移num位。每左移一個位,高階位都被移出(并且丢棄),并用0填充右邊。這意味着當左移的運算數是int 類型時,每移動1位它的第31位就要被移出并且丢棄;當左移的運算數是long 類型時,每移動1位它的第63位就要被移出并且丢棄。

在對byte 和short類型的值進行移位運算時,你必須小心。因為你知道Java 在對表達式求值時,将自動把這些類型擴大為 int 型,而且,表達式的值也是int 型。對byte 和short類型的值進行移位運算的結果是int 型,而且如果左移不超過31位,原來對應各位的值也不會丢棄。但是,如果你對一個負的byte 或者short類型的值進行移位運算,它被擴大為int 型後,它的符号也被擴充。這樣,整數值結果的高位就會被1填充。是以,為了得到正确的結果,你就要舍棄得到結果的高位。這樣做的最簡單辦法是将結果轉換為byte 型。下面的程式說明了這一點:

// Left shifting a byte value.

class ByteShift {

public static void main(String args[]) {

byte a = 64, b;

int i;

i = a << 2;

b = (byte) (a << 2);

System.out.println("Original value of a: " + a);

System.out.println("i and b: " + i + " " + b);

}

}

該程式産生的輸出下所示:

Original value of a: 64

i and b: 256 0

因變量a在指派表達式中,故被擴大為int 型,64(0100 0000 )被左移兩次生成值256 (10000 0000 )被賦給變量i。然而,經過左移後,變量b中惟一的1被移出,低位全部成了0,是以b的值也變成了0。

既然每次左移都可以使原來的操作數翻倍,程式員們經常使用這個辦法來進行快速的2 的乘法。但是你要小心,如果你将1移進高階位(31或63位),那麼該值将變為負值。下面的程式說明了這一點:

// Left shifting as a quick way to multiply by 2.

class MultByTwo {

public static void main(String args[]) {

int i;

int num = 0xFFFFFFE;

for(i=0; i<4; i++) {

num = num << 1;

System.out.println(num);

}

}

}

該程式的輸出如下所示:

536870908

1073741816

2147483632

-32

初值經過仔細選擇,以便在左移 4 位後,它會産生-32。正如你看到的,當1被移進31 位時,數字被解釋為負值。

4.2.3 右移運算符

右移運算符>>使指定值的所有位都右移規定的次數。它的通用格式如下所示:

value >> num

這裡,num 指定要移位值value 移動的位數。也就是,右移運算符>>使指定值的所有位都右移num位。下面的程式片段将值32右移2次,将結果8賦給變量a:

int a = 32;

a = a >> 2; // a now contains 8

當值中的某些位被“移出”時,這些位的值将丢棄。例如,下面的程式片段将35右移2 次,它的2個低位被移出丢棄,也将結果8賦給變量a:

int a = 35;

a = a >> 2; // a still contains 8

用二進制表示該過程可以更清楚地看到程式的運作過程:

00100011 35

>> 2

00001000 8

将值每右移一次,就相當于将該值除以2并且舍棄了餘數。你可以利用這個特點将一個整數進行快速的2的除法。當然,你一定要確定你不會将該數原有的任何一位移出。

右移時,被移走的最高位(最左邊的位)由原來最高位的數字補充。例如,如果要移走的值為負數,每一次右移都在左邊補1,如果要移走的值為正數,每一次右移都在左邊補0,這叫做符号位擴充(保留符号位)(sign extension ),在進行右移操作時用來保持負數的符号。例如,–8 >> 1 是–4,用二進制表示如下:

11111000 –8 >>1 11111100 –4

一個要注意的有趣問題是,由于符号位擴充(保留符号位)每次都會在高位補1,是以-1右移的結果總是–1。有時你不希望在右移時保留符号。例如,下面的例子将一個byte 型的值轉換為用十六

進制表示。注意右移後的值與0x0f進行按位與運算,這樣可以舍棄任何的符号位擴充,以便得到的值可以作為定義數組的下标,進而得到對應數組元素代表的十六進制字元。

// Masking sign extension.

class HexByte {

static public void main(String args[]) {

char hex[] = {

’0’, ’1’, ’2’, ’3’, ’4’, ’5’, ’6’, ’7’,

’8’, ’9’, ’a’, ’b’, ’c’, ’d’, ’e’, ’f’’

};

byte b = (byte) 0xf1;

System.out.println("b = 0x" + hex[(b >> 4) & 0x0f] + hex[b & 0x0f]);}}

該程式的輸出如下:

b = 0xf1

4.2.4 無符号右移

正如上面剛剛看到的,每一次右移,>>運算符總是自動地用它的先前最高位的内容補它的最高位。這樣做保留了原值的符号。但有時這并不是我們想要的。例如,如果你進行移位操作的運算數不是數字值,你就不希望進行符号位擴充(保留符号位)。當你處理像素值或圖形時,這種情況是相當普遍的。在這種情況下,不管運算數的初值是什麼,你希望移位後總是在高位(最左邊)補0。這就是人們所說的無符号移動(unsigned shift )。這時你可以使用Java 的無符号右移運算符>>> ,它總是在左邊補0。

下面的程式段說明了無符号右移運算符>>> 。在本例中,變量a被指派為-1,用二進制表示就是32位全是1。這個值然後被無符号右移24位,當然它忽略了符号位擴充,在它的左邊總是補0。這樣得到的值255被賦給變量a。

int a = -1; a = a >>> 24;

下面用二進制形式進一步說明該操作:

11111111 11111111 11111111 11111111 int型-1的二進制代碼>>> 24 無符号右移24位00000000 00000000 00000000 11111111 int型255的二進制代碼

由于無符号右移運算符>>> 隻是對32位和64位的值有意義,是以它并不像你想象的那樣有用。因為你要記住,在表達式中過小的值總是被自動擴大為int 型。這意味着符号位擴充和移動總是發生在32位而不是8位或16位。這樣,對第7位以0開始的byte 型的值進行無符号移動是不可能的,因為在實際移動運算時,是對擴大後的32位值進行操作。下面的例子說明了這一點:

// Unsigned shifting a byte value.

class ByteUShift {

static public void main(String args[]) {

進制表示。注意右移後的值與0x0f進行按位與運算,這樣可以舍棄任何的符号位擴充,以便得到的值可以作為定義數組的下标,進而得到對應數組元素代表的十六進制字元。

// Masking sign extension.

class HexByte {

static public void main(String args[]) {

char hex[] = {

’0’, ’1’, ’2’, ’3’, ’4’, ’5’, ’6’, ’7’,

’8’, ’9’, ’a’, ’b’, ’c’, ’d’, ’e’, ’f’’

};

byte b = (byte) 0xf1;

System.out.println("b = 0x" + hex[(b >> 4) & 0x0f] + hex[b & 0x0f]);}}

該程式的輸出如下:

b = 0xf1

4.2.4 無符号右移

正如上面剛剛看到的,每一次右移,>>運算符總是自動地用它的先前最高位的内容補它的最高位。這樣做保留了原值的符号。但有時這并不是我們想要的。例如,如果你進行移位操作的運算數不是數字值,你就不希望進行符号位擴充(保留符号位)。當你處理像素值或圖形時,這種情況是相當普遍的。在這種情況下,不管運算數的初值是什麼,你希望移位後總是在高位(最左邊)補0。這就是人們所說的無符号移動(unsigned shift )。這時你可以使用Java 的無符号右移運算符>>> ,它總是在左邊補0。

下面的程式段說明了無符号右移運算符>>> 。在本例中,變量a被指派為-1,用二進制表示就是32位全是1。這個值然後被無符号右移24位,當然它忽略了符号位擴充,在它的左邊總是補0。這樣得到的值255被賦給變量a。

int a = -1; a = a >>> 24;

下面用二進制形式進一步說明該操作:

11111111 11111111 11111111 11111111 int型-1的二進制代碼>>> 24 無符号右移24位00000000 00000000 00000000 11111111 int型255的二進制代碼

由于無符号右移運算符>>> 隻是對32位和64位的值有意義,是以它并不像你想象的那樣有用。因為你要記住,在表達式中過小的值總是被自動擴大為int 型。這意味着符号位擴充和移動總是發生在32位而不是8位或16位。這樣,對第7位以0開始的byte 型的值進行無符号移動是不可能的,因為在實際移動運算時,是對擴大後的32位值進行操作。下面的例子說明了這一點:

// Unsigned shifting a byte value.

class ByteUShift {

static public void main(String args[]) {

int b = 2;

int c = 3;

a |= 4;

b >>= 1;

c <<= 1;

a ^= c;

System.out.println("a = " + a);

System.out.println("b = " + b);

System.out.println("c = " + c);

}

}

該程式的輸出如下所示:

a = 3

b = 1

c = 6

---------------------------------------------

http://blog.csdn.net/xuchenguang/article/details/2301550

Java的移位操作(收集+糾正+排版)

此收集來源于網絡收集,是我整理、糾正以及排版,來為大家提供全面一點的知識講解,可能有些地方排的不好,望諒解。

======================================================================================

JAVA中位運算符包括:

&

|

~

^

<<

>>

>>>

一、

首先要搞清楚參與運算的數的位數:

(

聯想:java的8種基本類型:byte,short, char, int, long,float,double,boolean.

在記憶體中固定長度(位元組):1 2 2 4 8 4 8 true/false

這些固定類型的長度與具體的軟硬體環境無關。這一點與C++不同,Java中的char類型用Unicode碼儲存

與此對應的,java提供了8種包裝類型:

Byte,Short,Character,Integer,Long,Float,Double,Boolean.

它們之間的互相轉換:例如:

double a=1;

//把double基本類型轉換為Double包裝類型

Double b=new Double(a);

//把Double包裝類型轉換為double基本類型

a=b.doubleValue();

)

是以int的是32位。long的是64位。

如int i = 1;

i的二進制原碼表示為:

00000000000000000000000000000001

long l = 1;

l的二進制原碼表示為:

0000000000000000000000000000000000000000000000000000000000000001

二、

原碼——符号位為0表示正數,為1表示負數;

其餘各位等同于真值的絕對值。

如:0000000000000010B=2,1000000000000010B=-2

反碼——符号位的用法及正數的表示與“原碼”一樣;

負數的表示是在“原碼”表示的基礎上通過将符号位以外

的各位取反來獲得的。

如:0000000000000010B=2,1111111111111101B=-2

補碼——符号位的用法及正數的表示與“原碼”一樣;

負數的表示是在“反碼”的基礎上通過加1來獲得的。

如:00000010B=2,11111110B=-2

如int i = -1;

10000000000000000000000000000001,最高位是符号位。正數為0,負數為1。

符号位不變,其他位逐位取反後:

11111111111111111111111111111110,即反碼。

反碼加1:

11111111111111111111111111111111,即補碼。

==================================================================

注意:負數都是用補碼參與運算的。得到的也是補碼,需要減1取反獲得原碼。

千萬要了解這句話!!!

==================================================================

三、

常用的位運算符0在位運算中是比較特殊的。

 

& 與。 全1為1, 有0為0。  任何數與0與都等于0。  

| 或。 有1為1, 全0為0。  任何數與0或都等于原值。

~ 非。 逐位取反

^ 異或。 相同為0,相異為1。 任何數與0異或都等于原值。

對于int類型資料來說:

1.<<

邏輯左移,右邊補0,符号位就是被移動到的位.

正數:

x<<1一般相當于2x,但是可能溢出.

若x在這個範圍中: 2的30次方~(2的31次方-1) 二進制表示 0100...0000到0111...1111,<<後最高為變為1了,變成負數了.

負數:

x<<1一般也相當于2x,也有可能溢出.

若x在這個範圍中: -2的31次方~-(2的30次方+1)二進制表示1000...0000到1011...1111,<<後最高為變成0了,變成正數了.

2.>>

算術右移,和上面的不對應,為正數時左邊補0,為負數時左邊補1.

x>>1,相當于x/2,餘數被舍棄,因為這個是縮小,是以不會溢出.

不過有一點要注意: -1右移多少位都是-1.(這個道理很簡單嘛,呵呵)

另外舍棄的餘數是正的:

3>>1=1 舍棄的餘數是1.

-3>>1=-2 舍棄的餘數也是1,而不是-1.

對于正數 x>>1和x/2相等

對于負數 x>>1和x/2不一定相等.

3.>>>

邏輯右移,這個才是和<<對應的

這個把符号位一起移動,左邊補0

對于正數,>>>和>>是一樣的

對于負數,右移之後就變成正數了.

可以使用Integer.toBinaryString(int i)來看01比特,更加直覺.

四、

負數參與的運算,得到的是補碼,負數得到原碼的方法:

  方法一:将補碼先減1,再逐位取反,得到原碼。即為運算結果。

  方法二:将補碼先逐位取反,再加1,得到原碼。即為運算結果。

0例外,如果得到的是0,則不需這兩種方法,即得到的原碼位0。

另外,兩個正數運算後得到的就是原碼,不需要再用求原碼方法。

舉例:

-1^1,

-1

10000000000000000000000000000001--原碼

11111111111111111111111111111110--反碼

11111111111111111111111111111111--補碼

1

00000000000000000000000000000001--原碼

則-1^1等于

11111111111111111111111111111111^

00000000000000000000000000000001=

11111111111111111111111111111110--補碼

11111111111111111111111111111101--反碼

10000000000000000000000000000010--原碼==-2

即-1^1=-2

舉例:

-2^1

-2

10000000000000000000000000000010--原碼

11111111111111111111111111111101--反碼

11111111111111111111111111111110--補碼

1

00000000000000000000000000000001--原碼

則-2^-1等于

11111111111111111111111111111110^

00000000000000000000000000000001=

11111111111111111111111111111111--補碼

11111111111111111111111111111110--反碼

10000000000000000000000000000001--原碼==-1

下面的是cooltigerzsh(阿波羅) 于 2005-2-4 15:16:07對(<<、>>、 >>>)的一翻講解:

移位運算符面向的運算對象也是二進制的“位”。可單獨用它們處理整數類型(主類型的一種)。

左移位運算符(<<)能将運算符左邊的運算對象向左移動運算符右側指定的位數(在低位補0)。

“有符号”右移位運算符(>>)則将運算符左邊的運算對象向右移動運算符右側指定的位數。

“有符号”右移位運算符使用了“符号擴充”:若值為正,則在高位插入0;若值為負,則在高位插入1。

Java也添加了一種“無符号”右移位運算符(>>>),它使用了“零擴充”:無論正負,都在高位插入0。

這一運算符是C或C 沒有的。若對char,byte或者short進行移位處理,那麼在移位進行之前,它們會自動轉換成一個int。

隻有右側的5個低位才會用到。這樣可防止我們在一個int數裡移動不切實際的位數。

若對一個long值進行處理,最後得到的結果也是long。此時隻會用到右側的6個低位,防止移動超過long值裡現成的位數。

但在進行“無符号”右移位時,也可能遇到一個問題。若對byte或short值進行右移位運算,

得到的可能不是正确的結果(Java 1.0和Java 1.1特别突出)。它們會自動轉換成int類型,并進行右移位。

但“零擴充”不會發生,是以在那些情況下會得到-1的結果。

如:

public class URShift {

public static void main(String[] args) {

int i = -1;

i >>>= 10;

System.out.println(i);

long l = -1;

l >>>= 10;

System.out.println(l);

short s = -1;

s >>>= 10;

System.out.println(s);

byte b = -1;

b >>>= 10;

System.out.println(b);

}

}

輸出結果:

4194303

18014398509481983

-1

-1

===============================================================================

還有一點不得不提,也是非常隐含的一點,那就是我在Einstein的BLOG上找到的,他說是SCJP上的題,

摘錄他的文章如下:

SCJP裡的題還真是"噶"呀,很多都是讓人想不到的問題,有點意思.哈哈,今天最後一個,之後趴趴,太晚了,

明天還是去沈陽賣數位相機呢(興奮ing...)

下面代碼:

class test002

{

public static void main(String[] agrs)

{

int i=-1;

int j=i>>>32;

System.out.println(j);

}

}

按照我的了解應該輸出:0,因為JAVA的INT類型是占4位元組的,也就是說占32位,當右移了32位的時候所有的位應該都變成0,但輸出結果确是:-1,

想了很久沒想明白就上網發了個文章問了一下,非常感謝coffer283和danieljill()兩位朋友.

原來在JAVA進行移位運算中因為int是占32位,進行移位的數是32的模,是以當i>>>32的時候就等于i>>>0,相當于沒有進行移位.

我又試了試long類型的移位,long占8位元組也就是64位,是以移位的數是64的模.

---------------------------------------------------------------------------------------------------

上面是他的問題,給了我不少的啟發,對Java的移位運算有了跟深一層的了解。

同時我也對byte,short類型的移位周期做了實驗,也是32,跟int類型的相同,進而也驗證了byte、short進行右移位運算,會自動轉換成int類型,我并驗證了<<、>>、>>>這3個移位運算符都遵循移位周期。

=======================================================================================

呵呵,我現在終于整理完了,對Java移位運算有了個全面認識。我感覺沒有白花這幾個小時整理,hoho~~