要學習位元運算,必須要先學會二進制的數字表示法,
二進制與我們常用的十進制看起來非常不一樣,但核心概念是相同的。
二進制與十進制
二進制與十進制的「進」代表的是進位,而前面的數字代表的是遇到多少要進位,
十進制是遇到10就進位 ,二進制則是遇到2就進位,看吧!變兩位數了
所以其實也有八進制、十六進制等不同的進位方式。
數字的位數
我們如果去觀察一個十進制數字,其實可以發現最右邊(個位數)其實代表的是的數,往左一位(十位數)代表的數,以此類推。
二進制其實也是如此,但是把10都改成2,
例如二進制的1001
代表的是 。
大家應該都知道,電腦儲存資料的方法只有用0
和1
,而我們將一個儲存0
或1
的單位稱為位元Bit
,又將8個位元合稱為一個位元組Byte
。
如果去看資料型態的表格,你可以知道一個整數的記憶體空間是4 bytes
,也就是4x8 = 32 bits
其中是用二進制儲存的,而有1 bit
要被用來代表正負號,所以實際可以儲存的範圍是 ~ 。
NOT運算子 ~
使用方法: ~n
功能: 將一個數字內所有位元的1
變成0
,0
變成1
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 值 | ||
---|---|---|---|---|---|---|---|---|---|---|
n |
0 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 0 | 158 |
~n |
1 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 1 | -159 |
AND運算子 &
1 | 0 | |
---|---|---|
1 | 1 | 0 |
0 | 0 | 0 |
使用方法: a & b
功能: 將兩個數字內的位元位置對齊做AND邏輯運算(請見上表)
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 值 | ||
---|---|---|---|---|---|---|---|---|---|---|
a |
0 | 1 | 0 | 0 | 1 | 1 | 0 | 1 | 0 | 154 |
b |
0 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 1 | 57 |
a & b |
0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 24 |
OR運算子 |
1 | 0 | |
---|---|---|
1 | 1 | 1 |
0 | 1 | 0 |
使用方法: a | b
功能: 將兩個數字內的位元位置對齊做OR邏輯運算(請見上表)
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 值 | ||
---|---|---|---|---|---|---|---|---|---|---|
a |
0 | 1 | 0 | 0 | 1 | 1 | 0 | 1 | 0 | 154 |
b |
0 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 1 | 57 |
a | b |
0 | 1 | 0 | 1 | 1 | 1 | 0 | 1 | 1 | 187 |
XOR運算子 ^
1 | 0 | |
---|---|---|
1 | 0 | 1 |
0 | 1 | 0 |
使用方法: a ^ b
功能: 將兩個數字內的位元位置對齊做XOR邏輯運算(請見上表)
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 值 | ||
---|---|---|---|---|---|---|---|---|---|---|
a |
0 | 1 | 0 | 0 | 1 | 1 | 0 | 1 | 0 | 154 |
b |
0 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 1 | 57 |
a ^ b |
0 | 1 | 0 | 1 | 0 | 0 | 0 | 1 | 1 | 163 |
右移運算子 >>
使用方法: a >> b
功能: 將數字a
內的位元向右推移b
格 (左邊正數補0,負數補1,右邊自動丟棄)
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 值 | ||
---|---|---|---|---|---|---|---|---|---|---|
a |
0 | 1 | 0 | 0 | 1 | 1 | 0 | 1 | 0 | 154 |
a >> 2 |
0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 0 | 38 |
左移運算子 <<
使用方法: a << b
功能: 將數字a
內的位元向左推移b
格 (右邊自動補0)
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 值 | ||
---|---|---|---|---|---|---|---|---|---|---|
a |
0 | 0 | 0 | 0 | 1 | 1 | 0 | 1 | 0 | 26 |
a << 2 |
0 | 0 | 1 | 1 | 0 | 1 | 0 | 0 | 0 | 104 |
其實右移運算子
>>
及左移運算子<<
做的事情非常簡單,分別是將數值除以2的i次方(整數除法)以及乘以2的i次方而已,可以思考看看為什麼。
應用
判斷奇偶數:
前面(三元運算子)有稍微帶到判斷奇偶數可以使用 n & 1
,不過沒有解釋為什麼,
現在講到位元運算子就有辦法解釋了,你可以自己先想想看:
幾個重要的點:
1的二進制是 00000001
、只要是奇數,最後一個位元必定是1
、AND運算子
因為1
除了之外,其他都是0,所以AND的結果必定會是0
。
當我們對任何數n
做 n & 1
時,只會有項的區別,
而且我們知道奇數最後一個位元一定是1、偶數最後一個位元一定是0,
因此我們可以用n & 1
來判斷一個數的奇偶性。
2的i次方:
我們知道了左移運算子其實就是將數值乘以2,所以只要簡單的用1 << i
便可以達到2的i次方的目的。
2147483647(?
:
要做出 2147483647
只要使用 ~(1 << 31)
就可以了,也不會很難理解。
判斷兩數相等:
a ^ b == 0
兩數互換:
兩數互換有一種最簡單的方式,是使用:
但是還有一種不需要新變數的單行寫法 (XOR Swap Algorithm) :
這樣可能不太好理解,我們可以把它拆開來看:
引理:
XOR 位元運算具有下列性質
- 交換律:
- 結合律:
- 單位元素:
- 元素是自己的反元素: