Introduction
最近在寫leetcode的第190題,發現自己對Bitwise運算不太了解,所以想寫一篇文章當做學習的紀錄。Bitwise從名字來看就是要對bit做運算,對二進位的數字進行操作。這類運算常被用於嵌入式裝置這種記憶體受限的環境、權限管理的flag檢查,以及Unix檔案系統的檔案權限設置等實際應用場景。雖然工作上好像很少實際用到Bitwise 運算,但理解它不僅有助於解題,我想也會對系統底層的實現原理更有概念。
Bitwise所包含的operator如下:
- 右移
>> - 左移
<< - AND
& - OR
| - XOR
^ - AND NOT
&^(Go 特有)
Right Shift >>
將數字向右移, 左邊根據最高位置決定補上 0(正數)或 1(負數),等同於數字除以 2^n。
x := 20 // 00010100
x = x >> 2 // 00000101 = 5
Left Shift <<
將數字向左移動,等於數字乘以 2^n 。
x := 20 // 00010100
x = x << 2 // 01010000 = 80
AND &
對每個位元進行 AND 運算。只有當兩個位元都為 1 時,結果才為 1,否則為 0。
0 0 0 1 0 1 0 0 = 20
0 0 0 0 0 1 0 0 = 4
After & operations
0 0 0 0 0 1 0 0 = 4
x := 20 // 00010100
x := x & 4 //00010100 = 4
OR |
對每個位元進行 OR 運算。只要其中一個位元為 1,結果就為 1。
0 0 0 1 0 1 0 0 = 20
0 0 0 0 1 0 0 0 = 8
After | operations
0 0 0 1 1 1 0 0 = 28
x := 20 // 00010100
x = x | 8 // 00011100 = 28
XOR ^
對每個位元進行 XOR 運算。當兩個位元不同時,結果為 1;當兩個位元相同時,結果為 0。
0 0 0 1 0 1 0 0 = 20
0 0 0 0 1 0 1 0 = 10
After ^ operations
0 0 0 1 1 1 1 0 = 30
x := 20 // 00010100
y := 10 // 00001010
x = x ^ y // 00011110 = 30
AND NOT &^ (Go 特有)
先對右側運算元進行 NOT 運算 (將 0 變成 1,將 1 變成 0),然後再與左側運算元進行 AND 運算。
0 0 0 1 0 1 0 0 = 20
0 0 0 1 0 0 0 0 = 16
After &^ operations
0 0 0 0 0 1 0 0 = 4
x := 20 // 00010100
x = x &^ 16 // 00000100 = 4
Code Snippet
package main
import (
"fmt"
"strconv"
)
func main() {
var a uint = 20
fmt.Println("原始數值:", a)
fmt.Println("二進位表示:", strconv.FormatUint(uint64(a), 2)) // 10100
fmt.Println("========================= ")
// Right Shift 右移
fmt.Println("右移 (Right Shift) >>")
b := a >> 2
fmt.Println(a, ">> 2 =", b)
fmt.Println("二進位結果:", strconv.FormatUint(uint64(b), 2)) // 101
fmt.Println("========================= ")
// Left Shift 左移
fmt.Println("左移 (Left Shift) <<")
c := a << 2
fmt.Println(a, "<< 2 =", c)
fmt.Println("二進位結果:", strconv.FormatUint(uint64(c), 2)) // 1010000
fmt.Println("========================= ")
// AND 運算
fmt.Println("AND 運算 &")
d := a & 4
fmt.Println(a, "& 4 =", d)
fmt.Println("二進位:", strconv.FormatUint(uint64(a), 2), "&", strconv.FormatUint(uint64(4), 2))
fmt.Println("二進位結果:", strconv.FormatUint(uint64(d), 2)) // 100
fmt.Println("========================= ")
// OR 運算
fmt.Println("OR 運算 |")
e := a | 8
fmt.Println(a, "| 8 =", e)
fmt.Println("二進位:", strconv.FormatUint(uint64(a), 2), "|", strconv.FormatUint(uint64(8), 2))
fmt.Println("二進位結果:", strconv.FormatUint(uint64(e), 2)) // 11100
fmt.Println("========================= ")
// XOR 運算
fmt.Println("XOR 運算 ^")
f := a ^ 10
fmt.Println(a, "^ 10 =", f)
fmt.Println("二進位:", strconv.FormatUint(uint64(a), 2), "^", strconv.FormatUint(uint64(10), 2))
fmt.Println("二進位結果:", strconv.FormatUint(uint64(f), 2)) // 11110
fmt.Println("========================= ")
// AND NOT 運算
fmt.Println("AND NOT 運算 &^")
g := a &^ 16
fmt.Println(a, "&^ 16 =", g)
fmt.Println("二進位:", strconv.FormatUint(uint64(a), 2), "&^", strconv.FormatUint(uint64(16), 2))
fmt.Println("二進位結果:", strconv.FormatUint(uint64(g), 2)) // 100
fmt.Println("========================= ")
}
