Java 位運算符

Java 位運 算 符

位運算符用來對二進制位進行操作 ,Java中提 供 了 如 下所 示 的 位 運 算符 :

位 運 算 符 (>>,<<,>>>,&,|,^,~ ) ,位運 算 符 中 ,除 ~ 以 外 ,其餘 均 爲 二 元 運 算 符 。 操 作 數 只 能 爲 整 型 和字 符 型 數 據 。

基礎知識

補碼

所有的整數類型(除了char 類型之外)都是有符號的整數。這意味着他們既能表示正數,又能表示負數。 Java使用 補 碼 來 表 示 二 進 制 數 ,在補 碼 表 示 中 ,最高 位 爲 符號 位 ,正數 的 符 號 位 爲 0,負數 爲 1。補 碼 的 規 定 如 下 :

對 正 數 來 說 ,最高 位 爲 0,其餘 各 位 代 表 數 值 本 身 (以二 進制 表 示 ),如 +42的補碼 爲 00101010。

對 負 數 而 言 ,把該 數 絕 對 值 的 補 碼 按 位 取 反 ,然後 對 整 個數 加 1,即得 該 數的 補 碼 。 如 -42的補 碼 爲 11010110 (00101010 按 位 取 反 11010101 +1=11010110 )

用 補 碼 來 表 示 數 ,0的補 碼 是 唯 一 的 ,都爲 00000000。 (而在 原碼 ,反碼 表 示中 ,+0和 -0的表 示 是 不 唯 一 的 ,可參 見 相 應 的 書 籍 )。而 且 可 以用 111111表示 -1的補 碼 (這也 是 補 碼 與 原 碼 和 反 碼 的 區 別 )。


類型長度


整 型

整型常 量 在 機 器 中 佔 32位 ,具有 int型的 值 ,對於 long型值 ,則要在 數 字 後 加 L或 l,如 123L表示 一 個 長 整 數 ,它在 機 器 中 佔 64位。整 型 變 量 的 類 型 有 byte、 short、 int、 long四種 。 下面 列 出各 類 型 所 在 內 存 的 位 數 和 其表 示 範 圍 。

數據類型 描述 所佔位數
Integers

byte Byte-length integer 8-bit two's complement

short Short integer 16-bit two's complement

int Integer 32-bit two's complement


long

Long integer 64-bit two's complement


Real numbers

float Single-precision floating point 32-bit IEEE 754

double Double-precision floating point 64-bit IEEE 754


Other types

char A single character 16-bit Unicode character

boolean A boolean value (true or false) true or false


int類型 是 最 常 使 用 的 一 種 整 數 類 型 。 它 所 表 示的 數 據 範 圍 足 夠 大 ,而且 適 合 於 32位、64位處 理 器 。 但 對 於 大 型 計 算 ,常會 遇 到 很 大 的 整 數 ,超出 int類型 所 表 示 的 範 圍 ,這時 要 使 用long類型 。

由 於 不 同 的 機 器 對 於 多字 節 數 據 的 存 儲 方 式 不 同 ,可能 是 從 低 字 節 向 高 字 節 存 儲 ,也
可能 是 從 高 字 節 向 低 字 節 存 儲 ,這樣 ,在分 析 網 絡 協 議 或 文 件 格 式 時 ,爲了 解 決 不 同 機 器 上
的字 節 存 儲 順 序 問 題 ,用 byte類型 來 表 示 數 據 是 合 適 的 。 而 通 常 情 況 下 ,由於 其 表 示 的 數 據
範圍 很 小 ,容易 造 成 溢 出 ,應避 免 使 用 。

short類 型 則 很 少 使 用 ,它限 制 數 據 的 存 儲 爲 先 高 字 節 ,後低 字 節 ,這樣 在 某 些 機 器 中 會出錯 。

整型 變 量 的 定 義 ,如 :
byte b; //指定變量b爲byte型
short s; //指定變量s爲short型
int i; //指定變量i爲int型
long l; //指定變量l爲long型

浮 點 型 (實型 )數據
實型 變 量 的 類 型 有 float和 double兩種 ,下表 列 出 這 兩 種 類 型 所 佔 內 存 的 位 數 和 其 表示 範
圍。
數據類型 所佔位數 數的範圍
float 32 3.4e-038~3.4e+038
double 64 1.7e-308~1.7e+308

雙精 度 類 型 double比單 精 度 類 型 float具有 更 高 的 精 度 和 更 大 的 表 示 範 圍 ,常常 使 用 。

(三)實型 變 量 定 義 ,如

float f; //指 定 變 量 f爲 float型

double d; //指 定 變 量 d爲 double型

[注 ]與 C、 C++不同 ,Java中沒 有 無 符 號 型 整 數 ,而且 明 確 規 定 了 整 型 和 浮 點 型 數 據 所 佔 的
內存 字 節 數 ,這樣 就 保 證 了 安 全 性 、 魯 棒 性 和 平 臺 無 關 性。


Java 位運算符

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

運算符
結果

~
按位非(NOT)(一元運算)

&
按位與(AND)

|
按位或(OR)

^
按位異或(XOR)

>>
右移

>>>
右移,左邊空出的位以0填充 ;無符號右移

<<
左移

&=
按位與賦值

|=
按位或賦值

^=
按位異或賦值

>>=
右移賦值

>>>=
右移賦值,左邊空出的位以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


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

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);
}
}
該程序的輸出如下所示:
536870908
1073741816
2147483632
-32

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


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


無符號右移
正 如上面剛剛看到的,每一次右移,>>運算符總是自動地用它的先前最高位的內容補它的最高位。這樣做保留了原值的符號。但有時這並不是我們想要 的。例如,如果你進行移位操作的運算數不是數字值,你就不希望進行符號位擴展(保留符號位)。當你處理像素值或圖形時,這種情況是相當普遍的。在這種情況 下,不管運算數的初值是什麼,你希望移位後總是在高位(最左邊)補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


對於java移位運算的總結:

<!--[if !supportLists]-->1. <!--[endif]-->對於左移運算,每左移一個位,高階位都被移出(並且丟棄),並用0填充右邊。這意味着當左移的運算數是int 類型時,每移動1位,它的第31位就要被移出並且丟棄;當左移的運算數是long 類型時,每移動1位它的第63位就要被移出並且丟棄。

<!--[if !supportLists]-->2. <!--[endif]-->左移都可以使原來的操作數翻倍,程序員們經常使用這個辦法來進行快速的2 的乘法。但是你要小心,如果你將1移進高階位(31或63位),那麼該值將變爲負值。

<!--[if !supportLists]-->3. <!--[endif]-->在對byte 和short類型的值進行移位運算時 , Java將自動把這些類型擴大爲 int 型,而且,移位後的值也是int 型;如果左移不超過31位,原來對應各位的值不會丟棄。但是,如果你對一個負的byte 或者short類型的值進行移位運算,它被擴大爲int 型後,它的符號也被擴展,結果的高位就會被1填充。因此,爲了得到正確的結果,你就要捨棄得到結果的高位。這樣做的最簡單辦法是將移位運算的結果再轉換成byte 型 。

<!--[if !supportLists]-->4. <!--[endif]-->每右移一次,就相當於將該值除以2並且捨棄了餘數。你可以利用這個特點將一個整數進行快速的2的除法。當然,你一定要確保你不會將該數原有的任何一位移出。

<!--[if !supportLists]-->5. <!--[endif]-->無符號右移(>>>)與右移的區別:

<!--[if !supportLists]-->(1) <!--[endif]-->每一次右移,>>運算符總是自動地用它的先前最高位的內容補它的最高位。這樣做保留了原值的符號

<!--[if !supportLists]-->(2) <!--[endif]-->無符號移動總是在高位(最左邊)補0。

<!--[if !supportLists]-->6. <!--[endif]-->與C、C++不同,Java中沒有無符號型整數,而且明確規定了整型和浮點型數據所佔的內存字節數,這樣就保證了安全性、魯棒性和平臺無關性。

其他:

(1)BCD碼(二到十進制編碼)
人們通常習慣使用十進制數,而計算機內部多采用二進制表示和處理數值數據,因此在計算機輸入和輸出數據時,就要進行由十進制到二進制的轉換處理。

把十進制數的每一位分別寫成二進制形式的編碼,稱爲二進制編碼的十進制數,即二到十進制編碼或BCD(Binary Coded Decimal)編碼。

BCD碼編碼方法很多,通常採用8421編碼,這種編碼方法最自然簡單。其方法使用四位二進制數表示一位十進制數,從左到右每一位對應的權分別是23、22、21、20,即8、4、2、1。例如十進制數1975的8421碼可以這樣得出

1975(D)=0001 1001 0111 0101(BCD)

用四位二進制表示一位十進制會多出6種狀態,這些多餘狀態碼稱爲BCD碼中的非法碼。BCD碼與二進制之間的轉換不是直接進行的,當需要將BCD碼轉換成二進制碼時,要先將BCD碼轉換成十進制碼,然後再轉換成二進制碼;當需要將二進制轉換成BCD碼時,要先將二進制轉換成十進制碼,然後再轉換成BCD碼。
(2)字符編碼
在計算機中,對非數值的文字和其他符號進行處理時,首先要對其進行數字化處理,即用二進制編碼來表示文字和符號。字符編碼就是以二進制的數字來對應字符集的字符,目前用得最普遍的字符集是ANSI,對應ANSI字符集的二進制編碼就稱爲ANSI碼,DOS和Windows系統都使用了ANSI碼。在輸入過程中,系統自動將用戶輸入的各種數據按編碼的類型轉換成相應的二進制形式存入計算機存儲單元中;在輸出過程中,再由系統自動將二進制編碼數據轉換成用戶可以識別的數據格式輸出給用戶。

(3)ASCⅡ碼
用七位二進制表示字符的一種編碼,使用一個字節表示一個特殊的字符,字節高位爲0或用於在數據傳輸時的校驗。附表爲標準的ASCⅡ碼錶。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章