位運(yùn)算

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

編碼

原碼

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

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

反碼

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

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

補(bǔ)碼

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

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

有符號數(shù)與無符號數(shù)

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

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

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

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

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

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

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

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

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

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

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

6 - 5 = ?

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

-6 - 5 = ?

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

小結(jié)

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

位操作符

<<

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

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

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

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

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

// 例4:13 << 32
// 從上面的 例3 中可以后出,這是一個 32 位數(shù),如果我們向左移動 32 位會怎樣?
// 從輸出可以看出,其值為13,也就是說這個數(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
// 對應(yīng)原碼為:
// 1000 0000 0000 0000 0000 0000 1101 0000
// 對應(yīng)十進(jìn)制為:
// -208
System.out.println(-13 << 4); // 輸出:-208

>>

右移位,移動位置為從左至右第一個出現(xiàn) 1 的位置開始,高位根據(jù)符號位進(jìn)行補(bǔ)位,符號位是 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
// 對應(yīng)原碼:
// 1000 0000 0000 0000 0000 0000 0000 0001
// 對應(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
// 對應(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
// 對應(yīng)十進(jìn)制為:
// 1
System.out.println(7 >> 2);// 輸出:1

>>>

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

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

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

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

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

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

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

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

0 變 1,1 變 0,且這個位運(yùn)算符是一個單目運(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ǔ)碼相同,所以對應(yīng)十進(jìn)制為:
// 5

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

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

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

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

不管 & 前面的表達(dá)式的結(jié)果為 true 或 false 后面的表達(dá)式都會參加計算。只要 && 前面的表達(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á)式都會參加計算。
只要 || 前面的表達(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)

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

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

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

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

兩個數(shù)交換

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

求相反數(shù)

~x + 1

提高運(yùn)算效率

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

高級用法

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

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

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

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

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