位運算 看這篇就夠了
MENMORY TOAST
干貨篇
>>>>>>
前言
😬不知道大家是不是像我之前一樣一看到二進制丶十六進制就頭大,更別說位運算了,也不知道位運算的作用是啥,索性花了一些時間整理出這些知識點。
"我們知道程序中的所有數在計算機內存中都是以二進制的形式儲存的。位運算就是直接對整數在內存中的二進制位進行操作。
在系統軟件中,常常需要處理二進制位的問題。C語言提供了6個位操作運算符。這些運算符只能用於整型操作數,即只能用於帶符號或無符號的char,short,int與long類型。"
而二進制有8位(最低)丶 16位 丶32位 丶64位甚至更多,我們要怎樣進行 這令人愉悅的折磨呢 位運算呢?😇
X進制與二進制
既然位運算操作的是二進制數,就需要先把其他數轉換成二進制. 如果你已經知道如何進制轉換可以跳到位運算這段。
“首先看下十進制轉二進制
125轉換成二進制是01111101,計算方法很簡單正十進制數除以二,得到的商再除以二,依次類推直到商為零時為止,然后在旁邊標出各步的余數,最后倒着寫出來,再根據實際高位補零,即除二取余,倒序排列,高位補零。
/* 以下是計算過程
商/2 --------- 余數 */
125/2 --------- 1
62/2 --------- 0
31/2 --------- 1
15/2 --------- 1
7/2 --------- 1
3/2 --------- 1
1/2 --------- 1
0
將余數倒置則是1111101,然后我們發現結果只有7位數,而二進制最低位是8位,所以進行高位補零之后得到他的最終結果01111101
前置知識:被除數÷除數=商··· ···余數
例: 10 ÷ 3 =3... ... 1
二進制轉X進制
知道了十進制轉二進制,那你知道反過來該怎么算嗎?
將二進制中的位數分別與對應的值相乘,然后相加得到的就為十進制
我們看到01111101這個二進制,它是一個八位數,從低位(最右邊)開始計算,分別乘對應的值,最后相加,那如何區分一個二進制是正數還是負數呢?當然是看首位,首位為1為負數,首位為0為正數,這里的首位指的是二進制的頭一個數,而不是位數的最低位(計算是從低位開始,判斷正負則看首位)。
二進制: 0 1 1 1 1 1 0 1
第幾位: 7 6 5 4 3 2 1 0
=(1*2^0)+(0*2^1)+(1*2^2)+(1*2^3)+(1*2^4)+(1*2^5)+(1*2^6)+(0*2^7)
= 1+0+4+8+16+32+64+0
= 125
這樣就得出了結果125,然后首位為0則是正125。
其他進制轉二進制
8進制丶16進制 2進制等相互轉換其實是很簡單的,因為其有對應關系
8 = 2^3 (8→2)取一分三 (2→8)取三合一
16 = 2^4 (16→2)取一分四 (2→16)取四合一
32 = 2^5 (32→2)取一分五 (2→32)取五合一
...
//即二進制分組
二進制 01111101
分組(取四合一) 0111 1101
計算對應數 7 D
負數轉二進制
上面說的是正整數轉二進制的方法,那么負數呢?
如果你已經知道了一個正十進制數的二進制結果,那么他的負十進制數的二進制結果則是:
二進制取反,然后對結果再加一
如何理解呢,我們已經算出125的二進制(8位情況下)表示是01111101,取反的意思則是0變成1,1變成0,結果是10000010,然后加一,而二進制逢二進一,所以得出得結果為10000011,所以-125的二進制在8位的顯示中為10000011。
為什么要說在八位的顯示中?
如果是在16位 32位有何不同?
你可能會發現一個問題,如果用10000011反過來計算十進制的話,會發現他是131 而不是-125,為什么會這樣呢?
那是因為有符號和無符號的表示方式不同,在java中byte,short,int,long都是有符號的,他們也有對應的位數和范圍(以下^代表冪):
符號 | 占位 | 字節 | 范圍 |
---|---|---|---|
byte | 8 | 1 | -2^7 ~ 2^7-1(-128 ~ 127) |
short | 16 | 2 | -2^15 ~ 2^15-1 |
int | 32 | 4 | -2^31 ~ 2^31-1 |
long | 64 | 8 | -2^63 ~ 2^63-1 |
當定義八位(byte)-125時它代表的八位二進制是10000011,當你反過來算時得到的值131>八位(byte)的范圍,所以他並不能用byte表示。
byte a = 131; //錯誤 范圍超出
所以當10000011用來表示八位二進制是-125
正確算法應該是把正數轉負數的步驟逆向運算,正數轉負數時我們是取反再加一,反過來當范圍超需要表示為負數時減一取反才得出的結果才是正確的值,然后再根據首位為1是負數(轉換之前的首位),為0是正數確定符號。
(byte) 1000 0011 = -125
(short)0000 0000 1000 0011 = 131
(short)1111 1111 1000 0011 = -125
以上就是關於各進制與二進制之間的轉換了。
位運算
接下來我們看一下如何進行位運算,位運算到底有什么用呢?
" 大部分時候我們不會用到位運算,特別是碼農時代,明了直接才更有用。但是位運算就沒有用處了么?不是,位運算的作用大多體現在研究類運用。位運算生澀難懂,不過有個很明顯的優勢就是特別快。因為這是計算機看的語言,也是內部運算的語言."
*如果在平時編程當中如果正常的運算能更讓人理解的話是沒必要使用位運算表示的,強行使用無異於炫技罷了,代碼是讓人看的。
位取反(~)
位取反即之前負數轉二進制所用的方式,即0變1,1變0。
10001100
01110011
位與(&)
位與即如果兩個位進行比較兩位同時為1,結果才為1,否則結果為0。
例如: 125 & 7
125 & 7
二進制: 01111101 & 00000111
位與比較:
0 1 1 1 1 1 0 1
---------------
0 0 0 0 0 1 1 1
| | | | | | | |
× × × × × √ × √
| | | | | | | |
0 0 0 0 0 1 0 1
結果: 125&7 = 0000 0111 = 5
位或(|)
位或即如果兩個位進行比較兩位同時為0,結果才為0,否則結果為1。
例如: 125 | 7
125 | 7
二進制: 01111101 | 00000111
位或比較:
0 1 1 1 1 1 0 1
---------------
0 0 0 0 0 1 1 1
| | | | | | | |
√ × × × × × × ×
| | | | | | | |
0 1 1 1 1 1 1 1
結果: 125|7 = 0111 1111 = 7
異或(^)
位異或即如果兩個位進行比較相同取0,不同取1。
例如: 125 ^ 7
(java中^代表異或)
125 ^ 7
二進制: 01111101 ^ 00000111
位異或比較:
0 1 1 1 1 1 0 1
---------------
0 0 0 0 0 1 1 1
| | | | | | | |
√ × × × × √ × √
| | | | | | | |
0 1 1 1 1 0 1 0
結果: 125^7 = 0111 1010 = 122
異或的幾條性質:
1、交換律
2、結合律 (a^b)^c == a^(b^c)
3、對於任何數x,都有 x^x=0,x^0=x
4、自反性: a^b^b=a^0=a;
編程算法中的作用:交換兩個數(利用性質)
public void Swap(int &a, int &b){
if (a != b){
a ^= b; //a=a^b
b ^= a; //b=b^(a^b) = (b^b)^a = a
a ^= b; //a=(a^b)^a = (a^a)^b = b
}
}
右移(>>)
將一個數的各二進制位全部右移若干位,正數左補0,負數左補1,右邊丟棄。
125>>3
125 >> 3
右移:
0 1 1 1 1 1 0 1
---------------
0 0 1 1 1 1 1 0 |1 >> 1
0 0 0 1 1 1 1 1 |0 1 >> 2
0 0 0 0 1 1 1 1 |1 0 1 >> 3
^ ^ ^
正數補0負數補1
結果: 125>>3 = 0000 1111 = 15
等價與 125/2的3次方的商
左移(<<)
同樣的,左移則是將一個運算對象的各二進制位全部左移若干位(左邊的二進制位丟棄,右邊補0)。
125>>3
125 >> 3
右移:
0 1 1 1 1 1 0 1
---------------
0|1 1 1 1 1 0 1 0 << 1
0 1|1 1 1 1 0 1 0 0 << 2
0 1 1|1 1 1 0 1 0 0 0 << 3
^ ^ ^
補0
結果: 125<<3 = 1110 1000 = -24(八位)
125<<3 = 1110 1000 = 1000(十六位)
相當:125*2的3次方(若左移時舍棄的高位不包含1)
無符號右移(>>>)
無符號右移則始終補0,不考慮正負數。
總結:
符號 | 描述 | 運算規則 |
---|---|---|
~ | 反 | 1變0,0變1 |
& | 與 | 兩個位都為1時,結果才為1 |
| | 或 | 兩個位都為0時,結果才為0 |
^ | 異或 | 兩個位相同為0,相異為1 |
>> | 右移 | 各二進位全部左移若干位,高位丟棄,低位補0 |
<< | 左移 | 各二進位全部右移若干位,對無符號數,高位補0,有符號數,各編譯器處理方法不一樣,有的補符號位(算術右移),有的補0(邏輯右移) |
MENMORY TOAST >>>>>>