大家在閱讀一些算法或者一些開源框架的時(shí)候杰妓,總會(huì)見到~,>>埋涧,>>>板辽,|這種大量的位運(yùn)算,因此想要讀明白
這部分代碼棘催,對(duì)于計(jì)算機(jī)的二進(jìn)制操作以及位運(yùn)算是必須要了解的劲弦,那么本篇我們就開始詳細(xì)的學(xué)習(xí)二進(jìn)制
操作以及位運(yùn)算
整數(shù)的二進(jìn)制運(yùn)算
要理解整數(shù)的二進(jìn)制,我們先來看看最熟悉的十進(jìn)制醇坝,例如123這個(gè)值邑跪,如果按照十進(jìn)制,是如何計(jì)算出來的呢呼猪?其實(shí)123表示1x(10^2) + 2 x (10^1) + 3 x (10^0),表示的是各個(gè)位置上的數(shù)值含義之和画畅,每個(gè)位置的數(shù)值含義和所處的位數(shù)有關(guān)系,從右開始往左宋距,從個(gè)位開始轴踱,到高位,個(gè)位的數(shù)值則是(數(shù)值 x (10^0))乡革,每多一位數(shù)寇僧,則是10的次方多一位摊腋,也就是說,每一個(gè)位置都有一個(gè)位權(quán)嘁傀,從右到左兴蒸,第一位是1,第二位是10细办,第三位是100橙凳,以此類推。
而二進(jìn)制有何不同呢笑撞?首先十進(jìn)制每一位可以存在十個(gè)數(shù)值岛啸,分別為0-9,而在二進(jìn)制中茴肥,每一位只能存在0和1兩個(gè)值坚踩,同樣的二進(jìn)制也存在位權(quán)的概念,從右到左瓤狐,第一位為1瞬铸,然后每多一位依次乘以2,第二位為2础锐,第三位為4嗓节,以此類推,比如我們常見的十進(jìn)制數(shù)值2皆警,在二進(jìn)制中則為10表示拦宣,而數(shù)值3則是11表示,數(shù)值7則是111信姓,數(shù)值10則是1010鸵隧。
ps:計(jì)算規(guī)則則是拿十進(jìn)制的數(shù)值不停的除以2,如果能整除就是余數(shù)為0意推,則當(dāng)前位為0,不能整除則是余數(shù)為1左痢,當(dāng)前位為1,依次除到小于2為止系洛,這個(gè)時(shí)候我們將當(dāng)前值反過來俊性,則是對(duì)應(yīng)的二進(jìn)制值,例如:
42從上到下的結(jié)果為010101描扯,而計(jì)算完畢以后定页,則是從下往上讀取,則為101010绽诚,即二進(jìn)制的結(jié)果為101010
負(fù)數(shù)的二進(jìn)制
前面我們說了正整數(shù)的二進(jìn)制運(yùn)算規(guī)則典徊,為什么不是整個(gè)整數(shù)范圍的二進(jìn)制運(yùn)算呢杭煎?原來這個(gè)和計(jì)算機(jī)表示有關(guān)系,計(jì)算機(jī)無法讀取負(fù)數(shù)卒落,只能對(duì)應(yīng)做加法羡铲,所以當(dāng)一個(gè)負(fù)數(shù)轉(zhuǎn)為二進(jìn)制的時(shí)候,便有了新的表示法儡毕,我們稱之為補(bǔ)碼表示法
補(bǔ)碼/反碼/原碼
首先我們要明白一點(diǎn)也切,二進(jìn)制的首位用來表示正數(shù)還是負(fù)數(shù),如果是0則是正數(shù)腰湾,1為負(fù)數(shù)雷恃,剩下的才是對(duì)應(yīng)的值,但是需要注意的一點(diǎn)是费坊,計(jì)算機(jī)的計(jì)算中負(fù)數(shù)也會(huì)轉(zhuǎn)換為正數(shù)來計(jì)算倒槐,而轉(zhuǎn)換的過程,則是先獲取絕對(duì)值(正數(shù))得二進(jìn)制結(jié)果附井,此過程稱之為原碼讨越,然后給當(dāng)前的補(bǔ)碼每一位反過來,則是最終負(fù)數(shù)的結(jié)果羡忘,此過程稱之為反碼谎痢,接著通過給當(dāng)前反碼+1的結(jié)果,稱之為補(bǔ)碼卷雕,****节猿。當(dāng)然由于數(shù)值類型不同,字節(jié)數(shù)不同漫雕,我們知道每一個(gè)字節(jié)占8個(gè)比特位滨嘱,整數(shù)有四種類型,分別為byte浸间、short太雨、int、long魁蒜,分別占1囊扳、2、4兜看、8字節(jié)锥咸,即8、16细移、32搏予、64個(gè)比特位,所以當(dāng)位數(shù)不夠的時(shí)候弧轧,高位會(huì)補(bǔ)0(從左到右則是高位到低位)雪侥。
例如當(dāng)前值為-1碗殷,首先我們獲取絕對(duì)值1,首先是一個(gè)字節(jié)的值速缨,也就是8個(gè)比特位锌妻,1的二進(jìn)制原碼為1,前面補(bǔ)0鸟廓,則是00000001从祝,接著我們將當(dāng)前值反過來,得到反碼11111110引谜,然后對(duì)反碼+1則是補(bǔ)碼11111111牍陌,所以-1得值就是11111111。
例如-127员咽,絕對(duì)值為127毒涧,原碼為01111111,反碼為10000000贝室,補(bǔ)碼則是10000001契讲,其他的負(fù)數(shù)都是如此。
位運(yùn)算
前面介紹了整數(shù)的二進(jìn)制操作滑频,那么在計(jì)算機(jī)操作中捡偏,往往對(duì)二進(jìn)制輔以位運(yùn)算,以此來達(dá)到快速運(yùn)算的結(jié)果峡迷,接下來银伟,就開始學(xué)習(xí)計(jì)算機(jī)中的位運(yùn)算操作,首先將所有的位運(yùn)算列出來绘搞,如下:
運(yùn)算符 | 說明 |
---|---|
<< | 左移位彤避,在低位處補(bǔ)0 |
>> | 右移位,若為正數(shù)則高位補(bǔ)0夯辖,若為負(fù)數(shù)則高位補(bǔ)1 |
>>> | 無符號(hào)右移位琉预,無論正負(fù)都在高位補(bǔ)0 |
& | 與(AND),對(duì)兩個(gè)整型操作數(shù)中對(duì)應(yīng)位執(zhí)行布爾代數(shù)蒿褂,兩個(gè)位都為1時(shí)輸出1圆米,否則0。 |
| | 或(OR)啄栓,對(duì)兩個(gè)整型操作數(shù)中對(duì)應(yīng)位執(zhí)行布爾代數(shù)榨咐,兩個(gè)位都為0時(shí)輸出0,否則1谴供。 |
~ | 非(NOT),一元運(yùn)算符齿坷。 |
^ | 異或(XOR)桂肌,對(duì)兩個(gè)整型操作數(shù)中對(duì)應(yīng)位執(zhí)行布爾代數(shù)数焊,兩個(gè)位相等0,不等1崎场。 |
<<= | 左移位賦值。 |
>>= | 右移位賦值。 |
>>>= | 無符號(hào)右移位賦值娜遵。 |
&= | 按位與賦值乎澄。 |
|= | 按位或賦值。 |
^= | 按位異或賦值螃宙。 |
如此多的運(yùn)算蛮瞄,到底是如何計(jì)算的呢?
(<<)左移位
假設(shè)當(dāng)前有值int a = 5谆扎,操作為5 << 3挂捅,則是對(duì)5做左移三位的操作,那么具體操作步驟是什么呢?
1.將5轉(zhuǎn)為32比特位(int)的二進(jìn)制堂湖,得出結(jié)果0000 0000 0000 0000 0000 0000 0000 0101
2.這個(gè)時(shí)候?qū)⒄w朝左移動(dòng)三位闲先,超過三十二位的高位舍棄(少舍棄一位,留下一位作為正負(fù)數(shù)的符號(hào)位无蜂,即正數(shù)最高位補(bǔ)0伺糠,負(fù)數(shù)最高位補(bǔ)1),低位不足補(bǔ)0斥季,則是0000 0000 0000 0000 0000 0000 0010 1000 训桶,將當(dāng)前二進(jìn)制轉(zhuǎn)換為十進(jìn)制,則是40泻肯,所以最終計(jì)算的結(jié)果為40
public class LeftMoving{
public static void main(String[] args){
int a = 5;
System.out.println("5<<3="+(a << 3));//5<<3=40
}
}
(>>)右移位
假設(shè)當(dāng)前值int a = 5渊迁,操作為5>>1,則是對(duì)5右移1位的操作灶挟,具體步驟如下:
1.將5轉(zhuǎn)為32比特位的二進(jìn)制琉朽,結(jié)果為0000 0000 0000 0000 0000 0000 0000 0101
2.將整體二進(jìn)制的結(jié)果右移1位,如果本身為正數(shù)稚铣,首個(gè)最高位補(bǔ)0箱叁,負(fù)數(shù)首個(gè)最高位補(bǔ)1,其他高位補(bǔ)0惕医,超過的低位舍棄耕漱,結(jié)果為00000 0000 0000 0000 0000 0000 0000 010,將這個(gè)值轉(zhuǎn)為十進(jìn)制為2抬伺,所以最終結(jié)果為2
public class NegativeRightMoving{
public static void main(String[] args){
int a = 5;
System.out.println("5>>1="+(a>>1));//5>>1=2
}
}
(>>>)無符號(hào)右移
這里我們假設(shè)值int a = -5螟够,操作為-5>>>1,操作如下:
1.原碼為0000 0000 0000 0000 0000 0000 0000 0101
2.反碼為1111 1111 1111 1111 1111 1111 1111 1010
3.補(bǔ)碼為1111 1111 1111 1111 1111 1111 1111 1011
4.整體右移1位,高位補(bǔ)0妓笙,低位超過部分舍棄若河,則為0111 1111 1111 1111 1111 1111 1111 1101,轉(zhuǎn)為十進(jìn)制為2147483645 寞宫,所以最終結(jié)果為2147483645
public class UnsignedRightMoving{
public static void main(String[] args){
int a = -5;
System.out.println("-5>>>1="+(a>>>1));//-5>>>1=2147483645
}
}
ps:一定要分清楚無符號(hào)右移和右移操作的區(qū)別萧福,右移位運(yùn)算符>>,若操作的值為正辈赋,則在高位插入0鲫忍;若值為負(fù),則在高位插入1钥屈,右移補(bǔ)零操作符>>>悟民,無論正負(fù),都在高位插入0
(&)位與運(yùn)算符
假設(shè)值int a = 129焕蹄,操作為129&128逾雄,具體步驟如下:
1.129轉(zhuǎn)為二進(jìn)制0000 0000 0000 0000 0000 0000 1000 0001,128轉(zhuǎn)二進(jìn)制為0000 0000 0000 0000 0000 0000 1000 0000
2.從最高位開始比較腻脏,同一位的兩個(gè)值都為1則是1鸦泳,否則為0,則最終結(jié)果為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
}
(|)位或運(yùn)算符
假設(shè)值int a = 129做鹰,操作為129|128,具體步驟如下:
1.129轉(zhuǎn)為二進(jìn)制0000 0000 0000 0000 0000 0000 1000 0001鼎姐,128轉(zhuǎn)二進(jìn)制為0000 0000 0000 0000 0000 0000 1000 0000
2.從最高位開始比較钾麸,同一位的兩個(gè)值有一個(gè)為1則是1,否則為0炕桨,則最終結(jié)果為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
}
(~)位非運(yùn)算符
假設(shè)值為37饭尝,操作為~37,具體步驟如下:
1.37轉(zhuǎn)為二進(jìn)制為00000000 00000000 00000000 00100101
2.~操作是指如果每一位的值為0献宫,則是1钥平,如果為1,則是0姊途,所以整體取反涉瘾,結(jié)果為11111111 11111111 11111111 11011010
3.由于當(dāng)前獲取的結(jié)果首位為1,代表值為負(fù)數(shù)捷兰,那么我們通過當(dāng)前補(bǔ)碼計(jì)算原碼立叛,首先將補(bǔ)碼-1得到反碼,則為11111111 11111111 11111111 11011001贡茅,然后整體取反秘蛇,則為00000000 00000000 00000000 00100110 其做,轉(zhuǎn)為10進(jìn)制為38,由于是負(fù)數(shù)赁还,則最終結(jié)果為-38
public static void main(String[] args){
System.out.println("~37="+(~37));//~37=-38
}
(^)位異或運(yùn)算
假設(shè)當(dāng)前值為8庶柿,操作為8^11,具體操作如下:
1.8轉(zhuǎn)為二進(jìn)制0000 0000 0000 0000 0000 0000 0000 1000 秽浇,11轉(zhuǎn)為二進(jìn)制為0000 0000 0000 0000 0000 0000 0000 1011
2.^運(yùn)算符則是比較每一位的值是否一致,一致為0甚负,否則為1柬焕,所以比較的結(jié)果為0000 0000 0000 0000 0000 0000 0000 0011 ,轉(zhuǎn)為十進(jìn)制結(jié)果為3
public static void main(String[] args){
System.out.println("8^11="+(8^11));//8^11=3
}
(<<=)左移賦值
假設(shè)當(dāng)前有值int a = 5梭域,操作為a <<= 3斑举,那么具體操作步驟是什么呢?
1.先將5做左移三位的操作,得出結(jié)果為40(此步驟與<<一致)
2.然后將最終結(jié)果重新賦值給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
}
}
(>>=)右移賦值
假設(shè)當(dāng)前值int a = 5富玷,操作為a >>= 1,則是對(duì)5右移1位的操作既穆,具體步驟如下:
1.首先將a按照5 >> 1的操作赎懦,得到結(jié)果為2
2.將得到的結(jié)果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
}
}
(>>>=)無符號(hào)右移賦值
這里我們假設(shè)值int a = -5幻工,操作為a >>>= 1励两,操作如下:
1.5 >>> 1的結(jié)果為2147483645
2.將結(jié)果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
}
}
(&=)位與賦值
假設(shè)值int a = 129囊颅,操作為a &= 128当悔,具體步驟如下:
1.首先執(zhí)行129 & 128的操作,結(jié)果為128
2.把結(jié)果賦值給a踢代,則a = 128
public static void main(String[] args){
int a = 129;
a &= 128;//a = 129&128 = 128
System.out.println(a);//128
}
(|=)位或賦值
假設(shè)值int a = 129盲憎,操作為a |= 128,具體步驟如下:
1.首先執(zhí)行129 | 128胳挎,結(jié)果為129
2.將結(jié)果賦值給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
}
(^=)位異或賦值
假設(shè)當(dāng)前值a = 8,操作為a ^= 11串远,具體操作如下:
1.首先執(zhí)行8 ^ 11宏多,得到結(jié)果為3
2.將結(jié)果賦值給a,則a = 3
public static void main(String[] args){
int a = 8;
a ^= 11;//a = 8^11 = 3
System.out.println(a);//3
}