Java中的二進制與位運算

大家在閱讀一些算法或者一些開源框架的時候,總會見到~,>>,>>>,|這種大量的位運算,因此想要讀明白
這部分代碼,對于計算機的二進制操作以及位運算是必須要了解的,那么本篇我們就開始詳細的學習二進制
操作以及位運算

整數的二進制運算

要理解整數的二進制,我們先來看看最熟悉的十進制,例如123這個值,如果按照十進制,是如何計算出來的呢?其實123表示1x(10^2) + 2 x (10^1) + 3 x (10^0),表示的是各個位置上的數值含義之和,每個位置的數值含義和所處的位數有關系,從右開始往左,從個位開始,到高位,個位的數值則是(數值 x (10^0)),每多一位數,則是10的次方多一位,也就是說,每一個位置都有一個位權,從右到左,第一位是1,第二位是10,第三位是100,以此類推。

而二進制有何不同呢?首先十進制每一位可以存在十個數值,分別為0-9,而在二進制中,每一位只能存在0和1兩個值,同樣的二進制也存在位權的概念,從右到左,第一位為1,然后每多一位依次乘以2,第二位為2,第三位為4,以此類推,比如我們常見的十進制數值2,在二進制中則為10表示,而數值3則是11表示,數值7則是111,數值10則是1010。

ps:計算規則則是拿十進制的數值不停的除以2,如果能整除就是余數為0,則當前位為0,不能整除則是余數為1,當前位為1,依次除到小于2為止,這個時候我們將當前值反過來,則是對應的二進制值,例如:


十進制轉換二進制.png

42從上到下的結果為010101,而計算完畢以后,則是從下往上讀取,則為101010,即二進制的結果為101010

負數的二進制

前面我們說了正整數的二進制運算規則,為什么不是整個整數范圍的二進制運算呢?原來這個和計算機表示有關系,計算機無法讀取負數,只能對應做加法,所以當一個負數轉為二進制的時候,便有了新的表示法,我們稱之為補碼表示法

補碼/反碼/原碼

首先我們要明白一點,二進制的首位用來表示正數還是負數,如果是0則是正數,1為負數,剩下的才是對應的值,但是需要注意的一點是,計算機的計算中負數也會轉換為正數來計算,而轉換的過程,則是先獲取絕對值(正數)得二進制結果,此過程稱之為原碼,然后給當前的補碼每一位反過來,則是最終負數的結果,此過程稱之為反碼,接著通過給當前反碼+1的結果,稱之為補碼,****。當然由于數值類型不同,字節數不同,我們知道每一個字節占8個比特位,整數有四種類型,分別為byte、short、int、long,分別占1、2、4、8字節,即8、16、32、64個比特位,所以當位數不夠的時候,高位會補0(從左到右則是高位到低位)。

例如當前值為-1,首先我們獲取絕對值1,首先是一個字節的值,也就是8個比特位,1的二進制原碼為1,前面補0,則是00000001,接著我們將當前值反過來,得到反碼11111110,然后對反碼+1則是補碼11111111,所以-1得值就是11111111。

例如-127,絕對值為127,原碼為01111111,反碼為10000000,補碼則是10000001,其他的負數都是如此。

位運算

前面介紹了整數的二進制操作,那么在計算機操作中,往往對二進制輔以位運算,以此來達到快速運算的結果,接下來,就開始學習計算機中的位運算操作,首先將所有的位運算列出來,如下:

運算符 說明
<< 左移位,在低位處補0
>> 右移位,若為正數則高位補0,若為負數則高位補1
>>> 無符號右移位,無論正負都在高位補0
& 與(AND),對兩個整型操作數中對應位執行布爾代數,兩個位都為1時輸出1,否則0。
| 或(OR),對兩個整型操作數中對應位執行布爾代數,兩個位都為0時輸出0,否則1。
~ 非(NOT),一元運算符。
^ 異或(XOR),對兩個整型操作數中對應位執行布爾代數,兩個位相等0,不等1。
<<= 左移位賦值。
>>= 右移位賦值。
>>>= 無符號右移位賦值。
&= 按位與賦值。
|= 按位或賦值。
^= 按位異或賦值。

如此多的運算,到底是如何計算的呢?

(<<)左移位

假設當前有值int a = 5,操作為5 << 3,則是對5做左移三位的操作,那么具體操作步驟是什么呢?

1.將5轉為32比特位(int)的二進制,得出結果0000 0000 0000 0000 0000 0000 0000 0101

2.這個時候將整體朝左移動三位,超過三十二位的高位舍棄(少舍棄一位,留下一位作為正負數的符號位,即正數最高位補0,負數最高位補1),低位不足補0,則是0000 0000 0000 0000 0000 0000 0010 1000 ,將當前二進制轉換為十進制,則是40,所以最終計算的結果為40

public class LeftMoving{
    public static void main(String[] args){
           int a = 5;
           System.out.println("5<<3="+(a << 3));//5<<3=40
    }
}

(>>)右移位

假設當前值int a = 5,操作為5>>1,則是對5右移1位的操作,具體步驟如下:

1.將5轉為32比特位的二進制,結果為0000 0000 0000 0000 0000 0000 0000 0101

2.將整體二進制的結果右移1位,如果本身為正數,首個最高位補0,負數首個最高位補1,其他高位補0,超過的低位舍棄,結果為00000 0000 0000 0000 0000 0000 0000 010,將這個值轉為十進制為2,所以最終結果為2

public class NegativeRightMoving{
    public static void main(String[] args){
           int a = 5;
           System.out.println("5>>1="+(a>>1));//5>>1=2
    }
}

(>>>)無符號右移

這里我們假設值int a = -5,操作為-5>>>1,操作如下:

1.原碼為0000 0000 0000 0000 0000 0000 0000 0101

2.反碼為1111 1111 1111 1111 1111 1111 1111 1010

3.補碼為1111 1111 1111 1111 1111 1111 1111 1011

4.整體右移1位,高位補0,低位超過部分舍棄,則為0111 1111 1111 1111 1111 1111 1111 1101,轉為十進制為2147483645 ,所以最終結果為2147483645

public class UnsignedRightMoving{
    public static void main(String[] args){
           int a = -5;
           System.out.println("-5>>>1="+(a>>>1));//-5>>>1=2147483645 
    }
}

ps:一定要分清楚無符號右移和右移操作的區別,右移位運算符>>,若操作的值為正,則在高位插入0;若值為負,則在高位插入1,右移補零操作符>>>,無論正負,都在高位插入0

(&)位與運算符

假設值int a = 129,操作為129&128,具體步驟如下:

1.129轉為二進制0000 0000 0000 0000 0000 0000 1000 0001,128轉二進制為0000 0000 0000 0000 0000 0000 1000 0000

2.從最高位開始比較,同一位的兩個值都為1則是1,否則為0,則最終結果為0000 0000 0000 0000 0000 0000 1000 0000 ,即128

    public static void main(String[] args){
           System.out.println("129&128="+(129&128));//129&128=128
    }

(|)位或運算符

假設值int a = 129,操作為129|128,具體步驟如下:

1.129轉為二進制0000 0000 0000 0000 0000 0000 1000 0001,128轉二進制為0000 0000 0000 0000 0000 0000 1000 0000

2.從最高位開始比較,同一位的兩個值有一個為1則是1,否則為0,則最終結果為0000 0000 0000 0000 0000 0000 1000 0001即129

public static void main(String[] args){
           System.out.println("129|128="+(129|128));//129|128=129
    }

(~)位非運算符

假設值為37,操作為~37,具體步驟如下:

1.37轉為二進制為00000000 00000000 00000000 00100101

2.~操作是指如果每一位的值為0,則是1,如果為1,則是0,所以整體取反,結果為11111111 11111111 11111111 11011010

3.由于當前獲取的結果首位為1,代表值為負數,那么我們通過當前補碼計算原碼,首先將補碼-1得到反碼,則為11111111 11111111 11111111 11011001,然后整體取反,則為00000000 00000000 00000000 00100110 ,轉為10進制為38,由于是負數,則最終結果為-38

public static void main(String[] args){
           System.out.println("~37="+(~37));//~37=-38
    }

(^)位異或運算

假設當前值為8,操作為8^11,具體操作如下:

1.8轉為二進制0000 0000 0000 0000 0000 0000 0000 1000 ,11轉為二進制為0000 0000 0000 0000 0000 0000 0000 1011

2.^運算符則是比較每一位的值是否一致,一致為0,否則為1,所以比較的結果為0000 0000 0000 0000 0000 0000 0000 0011 ,轉為十進制結果為3

public static void main(String[] args){
           System.out.println("8^11="+(8^11));//8^11=3
    }

(<<=)左移賦值

假設當前有值int a = 5,操作為a <<= 3,那么具體操作步驟是什么呢?

1.先將5做左移三位的操作,得出結果為40(此步驟與<<一致)

2.然后將最終結果重新賦值給a,即a為40

public class LeftMoving{
    public static void main(String[] args){
           int a = 5;
           a <<= 3;//a = a << 3
           System.out.println(a);//40
    }
}

(>>=)右移賦值

假設當前值int a = 5,操作為a >>= 1,則是對5右移1位的操作,具體步驟如下:

1.首先將a按照5 >> 1的操作,得到結果為2

2.將得到的結果2賦值給a,即a=2

public class NegativeRightMoving{
    public static void main(String[] args){
           int a = 5;
           a >>= 1; //a = a >> 1;
           System.out.println(a);//2
    }
}

(>>>=)無符號右移賦值

這里我們假設值int a = -5,操作為a >>>= 1,操作如下:

1.5 >>> 1的結果為2147483645

2.將結果2147483645 賦值給a,則a=2147483645

public class UnsignedRightMoving{
    public static void main(String[] args){
           int a = -5;
           a >>>= 1;//a = 5 >>> 1 = 2147483645 
           System.out.println(a);//2147483645
    }
}

(&=)位與賦值

假設值int a = 129,操作為a &= 128,具體步驟如下:

1.首先執行129 & 128的操作,結果為128

2.把結果賦值給a,則a = 128

public static void main(String[] args){
          int a = 129;
          a &= 128;//a = 129&128 = 128
           System.out.println(a);//128
    }

(|=)位或賦值

假設值int a = 129,操作為a |= 128,具體步驟如下:

1.首先執行129 | 128,結果為129

2.將結果賦值給a,則a=129

public static void main(String[] args){
           int a = 129;
           a |= 128;//a = 129|128 = 129
           System.out.println("129|128="+(129|128));//129
    }

(^=)位異或賦值

假設當前值a = 8,操作為a ^= 11,具體操作如下:

1.首先執行8 ^ 11,得到結果為3

2.將結果賦值給a,則a = 3

public static void main(String[] args){
           int a = 8;
           a ^= 11;//a = 8^11 = 3
           System.out.println(a);//3
    }
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,837評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,196評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,688評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,654評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,456評論 6 406
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,955評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,044評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,195評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,725評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,608評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,802評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,318評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,048評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,422評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,673評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,424評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,762評論 2 372

推薦閱讀更多精彩內容