Java位運算知識點詳解
在日常的Java開發(fā)中,位運算使用的不多,使用的更多的是算數(shù)運算(+、-、*、/、%)、關系運算(<、>、<=、>=、==、!=)和邏輯運算(&&、||、!),所以相對來說對位運算不是那么熟悉,本文將以Java的位運算來詳細介紹下位運算及其應用。
1、 位運算起源
位運算起源于C語言的低級操作,Java的設計初衷是嵌入到電視機頂盒內(nèi),所以這種低級操作方式被保留下來。所謂的低級操作,是因為位運算的操作對象是二進制位,但是這種低級操作對計算機而言是非常簡單直接,友好高效的。在簡單的低成本處理器上,通常位運算比除法快得多,比乘法快幾倍,有時比加法快得多。雖然由于較長的指令流水線和其他架構設計選擇,現(xiàn)代處理器通常執(zhí)行加法和乘法的速度與位運算一樣快,但由于資源使用減少,位運算通常會使用較少的功率,所以在一些Java底層算法中,巧妙的使用位運算可以大量減少運行開銷。
2、 位運算詳解
Java位運算細化劃分可以分為按位運算和移位運算,見下表。
|
細化 |
符號 |
描述 |
運算規(guī)則 |
|
按位運算 |
& |
與 |
兩位都為1,那么結(jié)果為1 |
|
| |
或 |
有一位為1,那么結(jié)果為1 |
|
|
~ |
非 |
~0 = 1,~1 = 0 |
|
|
^ |
異或 |
兩位不相同,結(jié)果為1 |
|
|
移位運算 |
<< |
左移 |
各二進制位全部左移N位,高位丟棄,低位補0 |
|
>> |
右移 |
各二進制位全部右移N位,若值為正,則在高位插入 0,若值為負,則在高位插入 1 |
|
|
>>> |
無符號右移 |
各二進制位全部右移N位,無論正負,都在高位插入0 |
在進行位運算詳解之前,先來普及下計算機中數(shù)字的表示方法。對于計算機而言,萬物皆0、1,所有的數(shù)字最終都會轉(zhuǎn)換成0、1的表示,有3種體現(xiàn)形式,分別是:原碼、反碼和補碼。
原碼:原碼表示法在數(shù)字前面增加了一位符號位,即最高位為符號位,正數(shù)位該位為0,負數(shù)位該位為1.比如十進制的5如果用8個二進制位來表示就是00000101,-5就是10000101。
反碼:正數(shù)的反碼是其本身,負數(shù)的反碼在其原碼的基礎上,符號位不變,其余各個位取反。5的反碼就是00000101,而-5的則為11111010。
補碼:正數(shù)的補碼是其本身,負數(shù)的補碼在其原碼的基礎上,符號位不變,其余各位取反,最后+1。即在反碼的基礎上+1。5的反碼就是00000101,而-5的則為11111011。
了解了這幾個概念后,我們現(xiàn)在先記住一個結(jié)論,那就是在計算機系統(tǒng)中,數(shù)字一律用補碼來表示、運算和存儲,具體的原因可以看這篇文章的討論,這里不做更多討論,因為不是本文的重點。
2.1 與運算(&)
規(guī)則:轉(zhuǎn)為二進制后,兩位為1,則結(jié)果為1,否則結(jié)果為0。
舉例:
|
十進制 |
二進制(正數(shù)原碼、反碼、補碼一致) |
|
10 |
00000000000000000000000000001010 |
|
&12 |
&00000000000000000000000000001100 |
|
= |
= |
|
8 |
00000000000000000000000000001000 |
|
十進制 |
二進制(原碼) |
|
-6 |
10000000000000000000000000000110 |
|
&-2 |
&10000000000000000000000000000010 |
|
十進制 |
二進制(反碼) |
|
-6 |
11111111111111111111111111111001 |
|
&-2 |
&11111111111111111111111111111101 |
|
十進制 |
二進制(補碼) |
|
-6 |
11111111111111111111111111111010 |
|
&-2 |
&11111111111111111111111111111110 |
|
= |
= |
|
-6 |
11111111111111111111111111111010 |
最后的計算結(jié)果11111111111111111111111111111010還是補碼的形式,要看其十進制,還需要先轉(zhuǎn)成二進制原碼。
先轉(zhuǎn)反碼:11111111111111111111111111111010-1=11111111111111111111111111111001,得反碼11111111111111111111111111111001。
再轉(zhuǎn)原碼:在反碼的基礎上轉(zhuǎn)原碼,符號位不變,其他各位取反,得10000000000000000000000000000110。第一位1代表負數(shù),后面0110轉(zhuǎn)成十進制是6,得-6。
2.2 或運算(|)
規(guī)則:轉(zhuǎn)為二進制后,有一位為1,則結(jié)果為1,否則結(jié)果為0。
舉例:
|
十進制 |
二進制(正數(shù)原碼、反碼、補碼一致) |
|
10 |
00000000000000000000000000001010 |
|
|12 |
|00000000000000000000000000001100 |
|
= |
= |
|
14 |
00000000000000000000000000001110 |
|
十進制 |
二進制(原碼) |
|
-6 |
10000000000000000000000000000110 |
|
|-2 |
|10000000000000000000000000000010 |
|
十進制 |
二進制(反碼) |
|
-6 |
11111111111111111111111111111001 |
|
|-2 |
|11111111111111111111111111111101 |
|
十進制 |
二進制(補碼) |
|
-6 |
11111111111111111111111111111010 |
|
|-2 |
|11111111111111111111111111111110 |
|
= |
= |
|
-2 |
11111111111111111111111111111110 |
2.3 非運算(~)
規(guī)則:轉(zhuǎn)為二進制后,~0 = 1,~1 = 0。
舉例:
|
十進制 |
二進制(正數(shù)原碼、反碼、補碼一致) |
|
~7 |
~00000000000000000000000000000111 |
|
= |
= |
|
-8 |
11111111111111111111111111111000(補碼需轉(zhuǎn)換為原碼) |
11111111111111111111111111111000-1得反碼,可以把1000看成是0112,得反碼
11111111111111111111111111110111。根據(jù)反碼得原碼10000000000000000000000000001000。
|
十進制 |
二進制(原碼) |
|
~(-6) |
~10000000000000000000000000000110 |
|
十進制 |
二進制(反碼) |
|
~(-6) |
~11111111111111111111111111111001 |
|
十進制 |
二進制(補碼) |
|
~(-6) |
~11111111111111111111111111111010 |
|
= |
= |
|
5 |
00000000000000000000000000000101(正數(shù)原碼、反碼、補碼一致) |
2.4 異或運算(^)
規(guī)則:轉(zhuǎn)為二進制后,兩位不相同,結(jié)果為1,否則為0。
舉例:
|
十進制 |
二進制(正數(shù)原碼、反碼、補碼一致) |
|
15^2 |
00000000000000000000000000001111 ^00000000000000000000000000000010 |
|
= |
= |
|
13 |
00000000000000000000000000001101 |
2.5 左移運算(<<)
規(guī)則:轉(zhuǎn)為二進制后,各二進制位全部左移N位,高位丟棄,低位補0。
舉例:
|
十進制 |
二進制(正數(shù)原碼、反碼、補碼一致) |
|
2<<2 |
00000000000000000000000000000010 |
|
= |
0000000000000000000000000000001000 |
|
8 |
00000000000000000000000000001000 |
|
十進制 |
二進制(先取補碼 再對補碼操作位移) |
|
-2<<2 |
10000000000000000000000000000010(原碼) |
|
|
11111111111111111111111111111101(反碼) |
|
|
11111111111111111111111111111110(補碼) |
|
|
1111111111111111111111111111111000 |
|
|
11111111111111111111111111111000(補碼) |
|
|
11111111111111111111111111110111(反碼) |
|
-8 |
10000000000000000000000000001000(原碼) |
2.6 右移運算(>>)
規(guī)則:轉(zhuǎn)為二進制后,各二進制位全部右移N位,若值為正,則在高位插入 0,若值為負,則在高位插入 1。
舉例:
|
十進制 |
二進制(正數(shù)原碼、反碼、補碼一致) |
|
2>>2 |
00000000000000000000000000000010 |
|
= |
0000000000000000000000000000000010 |
|
0 |
00000000000000000000000000000000 |
|
十進制 |
二進制(先取補碼 再對補碼操作位移) |
|
-6>>2 |
10000000000000000000000000000110(原碼) |
|
|
11111111111111111111111111111001(反碼) |
|
|
11111111111111111111111111111010(補碼) |
|
|
1111111111111111111111111111111010 |
|
|
11111111111111111111111111111110(補碼) |
|
|
11111111111111111111111111111101(反碼) |
|
-2 |
10000000000000000000000000000010(原碼) |
2.7 無符號右移運算(>>>)
規(guī)則:轉(zhuǎn)為二進制后,各二進制位全部右移N位,無論正負,都在高位插入0。
舉例:
|
十進制 |
二進制(先取補碼 再對補碼操作位移) |
|
-1>>>1 |
10000000000000000000000000000001(原碼) |
|
|
11111111111111111111111111111110(反碼) |
|
|
11111111111111111111111111111111(補碼) |
|
|
011111111111111111111111111111111 |
|
|
01111111111111111111111111111111(補碼) |
|
|
01111111111111111111111111111110(反碼) |
|
溢出,只能表示到int的最大值2147483647 |
10000000000000000000000000000001(原碼) |
3、 應用
3.1 不用額外的變量實現(xiàn)兩個數(shù)字互換
見參考資料中的BitOperationTest,方法reverse通過三次異或操作完成了兩個變量值的替換。
證明很簡單,我們只需要明白異或運算滿足下面規(guī)律(實際不止如下規(guī)律):
0^a = a,a^a = 0;
a ^ b = b ^ a;
a ^ b ^ c = a ^ (b ^ c) = (a ^ b) ^ c;
a ^ b ^ a = b;
假設a,b兩個變量,經(jīng)過如下步驟完成值交換:a=a^b,b=b^a,a=a^b。
證明如下:
因為a ^ b = b ^ a,又a=a^b,b=b^a。故b=b^a= b^ (a^b)=a。
繼續(xù)a=a^b,a=(a^b) ^ b^ (a^b),故a=b。完成值交換。
3.2 不用判斷語句實現(xiàn)求絕對值
公式如下:(a^(a>>31))-(a>>31)
先整理一下使用位運算取絕對值的思路:若a為正數(shù),則不變,需要用異或0保持的特點;若a為負數(shù),則其補碼為原碼翻轉(zhuǎn)每一位后+1,先求其原碼,補碼-1后再翻轉(zhuǎn)每一位,此時需要使用異或1具有翻轉(zhuǎn)的特點。
任何正數(shù)右移31后只剩符號位0,最終結(jié)果為0,任何負數(shù)右移31后也只剩符號位1,溢出的31位截斷,空出的31位補符號位1,最終結(jié)果為-1.右移31操作可以取得任何整數(shù)的符號位。
那么綜合上面的步驟,可得到公式。a>>31取得a的符號,若a為正數(shù),a>>31等于0,a^0=a,不變;若a為負數(shù),a>>31等于-1 ,a^-1翻轉(zhuǎn)每一位。
3.3 判斷一個數(shù)的奇偶性
通過與運算判斷奇偶數(shù),偽代碼如下:
n&1 == 1?”奇數(shù)”:”偶數(shù)”
奇數(shù)最低位肯定是1,而1的二進制最低位也是1,其他位都是0,所以所有奇數(shù)和1與運算結(jié)果肯定是1。
相關文章
使用Cloud?Studio構建SpringSecurity權限框架(騰訊云?Cloud?Studio?實戰(zhàn)訓練
隨著云計算技術的成熟和普及,傳統(tǒng)編程能力和資源以云服務的形式開放出來,從中間件、數(shù)據(jù)庫等水平能力服務組件到人臉識別、鑒權服務等基本業(yè)務服務組件很容易的在云端獲取,本文介紹使用Cloud?Studio構建SpringSecurity權限框架的相關知識,感興趣的朋友一起看看吧2023-08-08
Java使用list集合remove需要注意的事項(使用示例)
List集合的一個特點是它其中的元素是有序的,也就是說元素的下標是根據(jù)插入的順序來的,在刪除頭部或者中間的一個元素后,后面的元素下標會往前移動,本文給大家介紹Java使用list集合remove需要注意的事項,感興趣的朋友一起看看吧2022-01-01
Java基于Dijkstra算法實現(xiàn)校園導游程序
這篇文章主要為大家詳細介紹了Java基于Dijkstra算法實現(xiàn)校園導游程序,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-03-03
Java 中Comparable和Comparator區(qū)別比較
本文,先介紹Comparable 和Comparator兩個接口,以及它們的差異;接著,通過示例,對它們的使用方法進行說明2013-09-09
Java利用Socket和IO流實現(xiàn)文件的上傳與下載
本文主要介紹了Java利用Socket和IO流實現(xiàn)文件的上傳與下載,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-04-04
Java?8函數(shù)式接口之Consumer用法示例詳解
這篇文章主要為大家介紹了Java?8函數(shù)式接口之Consumer用法示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-07-07

