位運(yùn)算

數(shù)據(jù)在計(jì)算機(jī)中都是以補(bǔ)碼形式存放的,位運(yùn)算也是以一個(gè)數(shù)的補(bǔ)碼進(jìn)行運(yùn)算,結(jié)果也自然也是一個(gè)補(bǔ)碼,這點(diǎn)在分析計(jì)算過程時(shí)非常重要。

編碼

原碼

原碼就是真正存儲(chǔ)的數(shù)值,比如存?zhèn)€ 7,那么它的所有進(jìn)制表示形式都應(yīng)該為 7,一個(gè)數(shù)的原碼也就做這個(gè)數(shù)的真值。例:

[+7] = [0000 0111]原碼或真值 = [0000 0111]補(bǔ)碼或機(jī)器數(shù)
[-7] = [1000 0111]原碼或真值 = [1111 1001]補(bǔ)碼或機(jī)器數(shù)

反碼

正數(shù)的反碼是其本身,負(fù)數(shù)的反碼是在其原碼基礎(chǔ)上,符號(hào)位不變,其余各個(gè)取反。對(duì)于反碼表示的是負(fù)數(shù),人腦是無(wú)法直觀的看出來(lái)它的數(shù)值,通常要將其轉(zhuǎn)換成原碼。例:

[+1] = [0000 0001]原 = [0000 0001]反
[-1] = [1000 0001]原 = [1111 1110]反

補(bǔ)碼

正數(shù)的補(bǔ)碼就是其本身,負(fù)數(shù)的補(bǔ)碼是在其原碼的基礎(chǔ)上,符號(hào)位不變,其余各位取反,最后加1(即反碼加1)。一個(gè)數(shù)的補(bǔ)碼也叫這個(gè)數(shù)的機(jī)器數(shù)。對(duì)于補(bǔ)碼表示的是負(fù)數(shù),人腦也是無(wú)法直觀的看出來(lái)它的數(shù)值,通常要將其轉(zhuǎn)換成原碼。例:

[+1] = [0000 0001]原碼或真值 = [0000 0001]反 = [0000 0001]補(bǔ)碼或機(jī)器數(shù)
[-1] = [1000 0001]原碼或真值 = [1111 1110]反 = [1111 1111]補(bǔ)碼或機(jī)器數(shù)

有符號(hào)數(shù)與無(wú)符號(hào)數(shù)

一個(gè)數(shù)有正負(fù)之分,計(jì)算機(jī)里則為有符號(hào)數(shù)與無(wú)符號(hào)數(shù)。人腦在計(jì)算中用 " + " 號(hào)表示正數(shù),用 " - " 減號(hào)表示負(fù)數(shù),但計(jì)算機(jī)不能把 " + " 、" - " 號(hào)顯示出來(lái)。計(jì)算機(jī)中用二進(jìn)制位最高位表示正負(fù)(0正1負(fù))。以8位 int 型為例:

// 無(wú)符號(hào) int
[00000000](最小 0)
[11111111](最大 2^7 = 127)

// 有符號(hào) int
[01111111](正數(shù)最大   2^7=127)
[11111111](負(fù)數(shù)最小 -(2^7 + 1) = -128)

注意:之所以負(fù)數(shù)會(huì)比正數(shù)多表示一個(gè)數(shù),是因?yàn)檎?(00000000),負(fù)0(10000000)都表示0,所以前輩們將負(fù)0這個(gè)狀態(tài)表示 -128

計(jì)算機(jī)計(jì)算兩個(gè)數(shù)的加減過程

對(duì)于數(shù)與數(shù)之間的運(yùn)算,人腦可以知道第一位是符號(hào)位, 在計(jì)算的時(shí)候我們會(huì)根據(jù)符號(hào)位, 選擇對(duì)真值區(qū)域的加減。 但是對(duì)于計(jì)算機(jī), 加減乘數(shù)已經(jīng)是最基礎(chǔ)的運(yùn)算, 要設(shè)計(jì)的盡量簡(jiǎn)單. 計(jì)算機(jī)辨別「符號(hào)位」顯然會(huì)讓計(jì)算機(jī)的基礎(chǔ)電路設(shè)計(jì)變得十分復(fù)雜! 于是人們想出了將符號(hào)位也參與運(yùn)算的方法. 我們知道,根據(jù)運(yùn)算法則減去一個(gè)正數(shù)等于加上一個(gè)負(fù)數(shù), 即: 1-1 = 1 + (-1) = 0 , 所以機(jī)器可以只有加法而沒有減法, 這樣計(jì)算機(jī)運(yùn)算的設(shè)計(jì)就更簡(jiǎn)單了。

分別用原碼、反碼、補(bǔ)碼計(jì)算 1 - 1:

// 用原碼運(yùn)算
// 結(jié)果顯然不對(duì)
[0000 0001]原 + [1000 0001]原 = [1000 0010]原 = -2

// 用反碼運(yùn)算
// 結(jié)果部分正確,雖然人類看起來(lái) +0 和 -0 是一樣的,但是 0 帶符號(hào)是沒有意義的,
// 而且會(huì)有 [0000 0000]原 和 [1000 0000]原兩種編碼表示0;
[0000 0001]反 + [1111 1110]反 = [1111 1111]反 = [1000 0000]原 = -0

// 用補(bǔ)碼運(yùn)算
// 0 用 [0000 0000]原 表示,-0 則不存在了,而且對(duì)于一個(gè) 8 位二進(jìn)制數(shù),
// 可以用 [1000 0000]原 表示 -128,即最小值。-128 沒有原碼和補(bǔ)碼表示。
[0000 0001]補(bǔ) + [1111 1111]補(bǔ) = [0000 0000]補(bǔ) = [0000 0000]原 = 0

從上面的運(yùn)算結(jié)果來(lái)看,只有用補(bǔ)碼運(yùn)算結(jié)果最準(zhǔn)確,下面進(jìn)一步用補(bǔ)碼運(yùn)算驗(yàn)證:

6 - 5 = ?

6  的補(bǔ)碼為:0000 0110
               +
-5 的補(bǔ)碼為:1111 1011
               =
           0000 0001 
正數(shù) 3 種編碼方式相同,所以原碼也為 0000 0001,對(duì)應(yīng)十進(jìn)制為:1

-6 - 5 = ?

-6  補(bǔ)碼為:1111 1010
                +
-5  補(bǔ)碼為:1111 1011
                =
           1111 0101 
補(bǔ)碼為負(fù)數(shù),原碼為:1000 1011 ,對(duì)應(yīng)十進(jìn)制為:-11

小結(jié)

  • 一個(gè)正數(shù)的原碼、反碼、補(bǔ)碼 3 種編碼方式結(jié)果相同,而對(duì)于一個(gè)負(fù)數(shù),3 種編碼方式是完全不同的。
  • [負(fù)數(shù)]原 = 負(fù)數(shù)補(bǔ)碼再求補(bǔ)碼
  • 在計(jì)算機(jī)內(nèi)部,二進(jìn)制加法是基本運(yùn)算,而二進(jìn)制的減法則是采用補(bǔ)碼運(yùn)算,將減法轉(zhuǎn)換成加法實(shí)現(xiàn)的;二進(jìn)制乘、除法運(yùn)算可以通過加、減和移位來(lái)實(shí)現(xiàn)。 二進(jìn)制數(shù)中小數(shù)點(diǎn)向右移 1 位,數(shù)值就擴(kuò)大2倍;小數(shù)點(diǎn)向左移 1 位,數(shù)值就縮小 2 倍。

位操作符

<<

左移位,移動(dòng)位置為從左至右第一個(gè)出現(xiàn) 1 的位置開始,低位補(bǔ) 0,高位丟失。如需要了解移動(dòng)過程,請(qǐng)參考下面的例子:

// 下面以數(shù)字 13 為例,因?yàn)?13 為正數(shù),所以原碼、反碼、補(bǔ)碼都相同,即:
// 13[原][反][補(bǔ)] = 0000 1101

// 例1:13 << 2
// 0000 1101 左移 2 位得到的結(jié)果為:0011 0100
// 很明顯,0011 0100 為一正數(shù),所以其對(duì)應(yīng)的原碼也是 0011 0100,對(duì)應(yīng)的十進(jìn)制為:52
System.out.println(13 << 2);// 輸出:52

// 例2:13 << 4
// 0000 1101 左移 4 位得到的結(jié)果補(bǔ)碼為:1101 0000
// 因?yàn)?1101 0000 是一正數(shù),所以其對(duì)應(yīng)的原碼也是 1101 0000,對(duì)應(yīng)的十進(jìn)制為:208
// 
// 「 注意 」?。。。。。?// 1101 0000 中 的 1101 并不是處于最高位,它前面還有多位,只是我們?yōu)榱朔奖銢]寫出來(lái)。
// 下面一個(gè)例子將驗(yàn)證最高位到底在哪一位
System.out.println(13 << 4);// 輸出:208

// 例3:13 << 28
// 首先,一個(gè)數(shù)有可能是32位、64位,我們猜測(cè)它是 32 位的,那么 13 的補(bǔ)碼就該寫為:
// 0000 0000 0000 0000 0000 0000 0000 1101
// 如果這個(gè)數(shù)是 32 位的,且 1101 要處于最高位,那么觀察上面的數(shù),至少需要左移 28 位,移動(dòng)后為:
// 1101 0000 0000 0000 0000 0000 0000 0000
// 注意,移位后的這個(gè)數(shù)依然是一個(gè)補(bǔ)碼,如果我們猜測(cè)的 32 位數(shù)沒錯(cuò),那么最高位就應(yīng)該是符號(hào)位
// 左移 28 位后應(yīng)該就為一個(gè)負(fù)數(shù)了,其原碼為:
// 1011 0000 0000 0000 0000 0000 0000 0000,對(duì)應(yīng)的十進(jìn)制數(shù)為:-805306368
// 從下面的輸出來(lái)看,我們的猜測(cè)是正確的
//
// 「 注意 」?。。。。。?// 有可能和具體設(shè)備有關(guān),但難思路都相同
System.out.println(13 << 28);// 輸出:-805306368

// 例4:13 << 32
// 從上面的 例3 中可以后出,這是一個(gè) 32 位數(shù),如果我們向左移動(dòng) 32 位會(huì)怎樣?
// 從輸出可以看出,其值為13,也就是說(shuō)這個(gè)數(shù)又回到了正常的形式,即:
// 0000 0000 0000 0000 0000 0000 0000 1101
System.out.println(13 << 32);// 輸出:13

// 例5:-13 << 4
// -13 的補(bǔ)碼為:
// 1111 1111 1111 1111 1111 1111 1111 0011
// 左移 4 位后:
// 1111 1111 1111 1111 1111 1111 0011 0000
// 對(duì)應(yīng)原碼為:
// 1000 0000 0000 0000 0000 0000 1101 0000
// 對(duì)應(yīng)十進(jìn)制為:
// -208
System.out.println(-13 << 4); // 輸出:-208

>>

右移位,移動(dòng)位置為從左至右第一個(gè)出現(xiàn) 1 的位置開始,高位根據(jù)符號(hào)位進(jìn)行補(bǔ)位,符號(hào)位是 0 補(bǔ) 0,是 1 補(bǔ) 1,低位丟失。例:

// 例1:-13 >> 4
// -13 的補(bǔ)碼為:
// 1111 1111 1111 1111 1111 1111 1111 0011
// 右移 4 位后:
// 1111 1111 1111 1111 1111 1111 1111 1111
// 對(duì)應(yīng)原碼:
// 1000 0000 0000 0000 0000 0000 0000 0001
// 對(duì)應(yīng)十進(jìn)制為:
// -1
System.out.println(-13 >> 4); // 輸出:-1

// 例2:13 >> 4
// 13 的補(bǔ)碼為:
// 0000 0000 0000 0000 0000 0000 0000 1101
// 右移 4 位后
// 0000 0000 0000 0000 0000 0000 0000 0000
// 對(duì)應(yīng)十進(jìn)制為:
// 0
System.out.println(13 >> 4);// 輸出:0

// 例3:7 >> 2
// 7 的補(bǔ)碼為:
// 0000 0000 0000 0000 0000 0000 0000 0111
// 右移 2 位后
// 0000 0000 0000 0000 0000 0000 0000 0001
// 對(duì)應(yīng)十進(jìn)制為:
// 1
System.out.println(7 >> 2);// 輸出:1

>>>

無(wú)符號(hào)右移,只對(duì) 32 位與 64 位數(shù)有意義。對(duì)于正數(shù),>> 與 >>> 結(jié)果都是一樣的; 負(fù)數(shù)時(shí),>> 高位用 1 補(bǔ)上,>>> 高位用 0 補(bǔ)上。

& (位與運(yùn)算符)

只有 1 & 1 = 1,其它都為 0。例:

// 6 & 5
// 6對(duì)應(yīng)補(bǔ)碼為:     0000 0110
//                     &
// 5對(duì)應(yīng)補(bǔ)碼為:     0000 0101 
//                     =
// 補(bǔ)碼結(jié)果為:      0000 0100
// 正數(shù)的 3 種編碼方式相同,所以 0000 0100 原碼也是 0000 0100 ,對(duì)應(yīng)十進(jìn)制為:4

| (位或運(yùn)算符)

只有 0 | 0 = 0,其它都為 1。例:

// 6 | 5
// 6對(duì)應(yīng)補(bǔ)碼為:     0000 0110
//                     |
// 5對(duì)應(yīng)補(bǔ)碼為:     0000 0101 
//                     =
// 補(bǔ)碼運(yùn)算結(jié)果為:   0000 0111
// 正數(shù)的 3 種編碼方式相同,所以 0000 0111 的原碼也為 0000 0111,對(duì)應(yīng)的十進(jìn)制為:7

~ (位非運(yùn)算符)

0 變 1,1 變 0,且這個(gè)位運(yùn)算符是一個(gè)單目運(yùn)算符,不能寫成 x ~ y 的形式。例:

// 例 1:~-6
// -6 原碼為:
// 1000 0000 0000 0000 0000 0000 0000 0110
// 補(bǔ)碼為:
// 1111 1111 1111 1111 1111 1111 1111 1010
// 進(jìn)行位非運(yùn)算后:
// 0000 0000 0000 0000 0000 0000 0000 0101
// 正數(shù)原碼、反碼、補(bǔ)碼相同,所以對(duì)應(yīng)十進(jìn)制為:
// 5

^ (位異或運(yùn)算符)

1 ^ 0 = 1,其它都為 0。例:

// 6^5
// 6對(duì)應(yīng)補(bǔ)碼為:     0000 0110
//                     ^
// 5對(duì)應(yīng)補(bǔ)碼為:     0000 0101
//                     =    
// 補(bǔ)碼運(yùn)算結(jié)果為:   0000 0011
// 正數(shù)的 3 種編碼方式相同,所以 0000 0011 的原碼也為 0000 0011,對(duì)應(yīng)的十進(jìn)制為:3

& 做邏輯運(yùn)算符與 && 的區(qū)別

不管 & 前面的表達(dá)式的結(jié)果為 true 或 false 后面的表達(dá)式都會(huì)參加計(jì)算。只要 && 前面的表達(dá)式為 false,則后面的表達(dá)式不參加運(yùn)算。例:

int i = 1;
boolean b = i++ > 5 & i++ < 10;
// 結(jié)果為:b = false;i = 3;

boolean bb = i++ > 5 && i++ < 10;
// 結(jié)果為:bb = false;i = 2;

| 做邏輯運(yùn)算符與 || 的區(qū)別

不管 | 前面的表達(dá)式的結(jié)果為 true 或 false 后面的表達(dá)式都會(huì)參加計(jì)算。
只要 || 前面的表達(dá)式為 true,則后面的表達(dá)式不參加運(yùn)算。例:

int i = 1;
boolean b = i++ < 5 | i++ < 10;
// 結(jié)果為:b = true;i = 3;

boolean bb = i++ < 5 || i++ < 10;
// 結(jié)果為:bb = true;i = 2;

位運(yùn)算用途

乘以、除以 2^n

x << n(乘以 2^n)
x >> n(除以 2^n)

判斷一個(gè)數(shù)是奇數(shù)還是偶數(shù)

x & 1(結(jié)果只有 0 和 1 兩種情況,0 為偶數(shù),1 為奇數(shù))
x & 2 (結(jié)果只有 0 和 2 兩種情況,可做兩種條件的判斷)

求兩個(gè)數(shù)的平均數(shù)

(x & y) + ( ( x ^ y ) >> 1)

兩個(gè)數(shù)交換

x ^= y;
y ^= x;
x ^= y;

求相反數(shù)

~x + 1

提高運(yùn)算效率

可提高運(yùn)算效率,處理器能夠直接支持

高級(jí)用法

權(quán)限管理、數(shù)據(jù)加密等,利用位運(yùn)算可做的東西深不可測(cè)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • ▲ 首先需要了解一些計(jì)算機(jī)的基礎(chǔ): 對(duì)于一個(gè)有符號(hào)的數(shù)(能表示正負(fù)),最高位就是符號(hào)位,其中0代表正數(shù)、1代表負(fù)數(shù)...
    sushengren閱讀 355評(píng)論 0 0
  • 網(wǎng)站亂碼問題我們會(huì)經(jīng)常碰到,大多見于非英文的中文字符或其他字符亂碼,而且,這類問題常常是因?yàn)榫幋a方式問題,主要原因...
    波段頂?shù)?/span>閱讀 3,349評(píng)論 1 9
  • 本篇文章講解了計(jì)算機(jī)的原碼, 反碼和補(bǔ)碼. 并且進(jìn)行了深入探求了為何要使用反碼和補(bǔ)碼, 以及更進(jìn)一步的論證了為何可...
    yang2yang閱讀 2,487評(píng)論 1 13
  • 文「徐比比比比比比」 她在微信上的昵稱叫做24號(hào)姑娘。 她一直覺得24這個(gè)數(shù)字能都帶給她好運(yùn),是她的獨(dú)家幸運(yùn)數(shù)字。...
    徐比比比比比比閱讀 795評(píng)論 0 3
  • 主講人介紹 蒲博士,統(tǒng)計(jì)學(xué)博士2007年-2011年在上海交大讀核工程專業(yè)2011年-2013年在明尼蘇達(dá)大學(xué)讀物...
    萬(wàn)能滴小籠包閱讀 1,408評(píng)論 2 4

友情鏈接更多精彩內(nèi)容