Go 中的位元運算

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。![cover](https://raw.githubusercontent.com/studygolang/gctt-images/master/go-bits/cover.png)在以前記憶體和處理能力(CPU)都是非常昂貴的,於是直接在位上編程就成為了處理資訊的首選方式(在有些情況下也是唯一的方式)。如今,直接對位進行操作在底層系統、影像處理和密碼學等領域還是至關重要的。在 Go 語言中支援以下幾種操作位的方式:``` & 位與 | 位或 ^ 異或&^ 位與非<< 左移>> 右移```接下來我們會對每一個操作符進行詳細的討論並給出一些可以應用位操作的執行個體。## `&` 操作符在 Go 中,`&` 操作符用來在兩個整數之間進行位 AND 運算。AND 操作有以下特性:```Given operands a, bAND(a, b) = 1; only if a = b = 1 else = 0// 給定 2 個運算元 a,b:// 若且唯若 a 和 b 都為 1 時,操作 AND(a, b) 的結果為 1。// 否則操作 AND(a, b) 為 0。```AND 操作符是一個很好的將整數的指定位清零的方式。在下面的例子中,我們使用 `&` 運算子將數字後 4 位清零。```gofunc main() { var x uint8 = 0xAC // x = 10101100 x = x & 0xF0 // x = 10100000}```所有的二進位操作符都支援簡寫形式,我們可以把上面的例子改為簡寫形式:```gofunc main() { var x uint8 = 0xAC // x = 10101100 x &= 0xF0 // x = 10100000}```另外一個小技巧就是可以通過 `&` 來判斷一個數字是奇數還是偶數。我們可以將數字和值 1 使用 `&` 做 AND 運算。如果結果是 1,那說明原來的數字是一個奇數。```goimport ( "fmt" "math/rand")func main() { for x := 0; x < 100; x++ { num := rand.Int() if num&1 == 1 { fmt.Printf("%d is odd\n", num) } else { fmt.Printf("%d is even\n", num) } }}```[線上運行](https://play.golang.org/p/2mTNOtioNM)## `|` 操作符`|` 用來做數位位 OR 運算。OR 操作符有以下特性:```Given operands a, bOR(a, b) = 1; when a = 1 or b = 1 else = 0 // 給定兩個運算元 a,b// 若且唯若 a 和 b 均為 0 時,操作 OR(a, b) 返回 0。// 否者返回 1。```我們可以使用這個特性來將一個整數中的指定位置為 1。在下面的例子裡,我們使用 OR 運算將第 3、7、8 位置為 1。```gofunc main() { var a uint8 = 0 a |= 196 fmt.Printf("%b", a)}// prints 11000100 ^^ ^ ```[線上運行](https://play.golang.org/p/3VPv4D83Oj)當對一個數字使用掩碼技術,OR 是非常有用的。下面的例子我們可以設定更多的位:```gofunc main() { var a uint8 = 0 a |= 196 a |= 3 fmt.Printf("%b", a)}// prints 11000111```[線上運行](https://play.golang.org/p/7aJLwh3y4x)在上面的例子中,我們不僅有數字 196 中的所有位,而且最後的兩位也被數字 3 置 1。我們可以一直進行置 1 操作,直到所有的位都為 1。### 使用位作為配置資訊現在,回顧 `AND(a,1) = a if and only if a = 1`。我們可以使用這個技巧來查詢指定位上的值。例如 `a & 196` 將會返回 `196`,因為在 `a` 中 `196` 的所有位都被置 1 了。所以我們能夠使用 OR 和 AND 來設定和讀取配置資訊的值。 下面的程式碼完成了這個功能。函數 `procstr` 轉換給定的字串。它接收兩個參數:第一個參數 `str` 是一個要被轉換的字串,第二個參數 `conf` 使用掩碼指定轉換時的配置資訊。```go const ( UPPER = 1 // upper case LOWER = 2 // lower case CAP = 4 // capitalizes REV = 8 // reverses)func main() { fmt.Println(procstr("HELLO PEOPLE!", LOWER|REV|CAP))}func procstr(str string, conf byte) string { // reverse string rev := func(s string) string { runes := []rune(s) n := len(runes) for i := 0; i < n/2; i++ { runes[i], runes[n-1-i] = runes[n-1-i], runes[i] } return string(runes) } // query config bits if (conf & UPPER) != 0 { str = strings.ToUpper(str) } if (conf & LOWER) != 0 { str = strings.ToLower(str) } if (conf & CAP) != 0 { str = strings.Title(str) } if (conf & REV) != 0 { str = rev(str) } return str}```[線上運行](https://play.golang.org/p/4E05PQwj5q) 調用 `procstr("HELLO PEOPLE!", LOWER|REV|CAP)` 將會把字串轉換成小寫,反轉並將每個單詞的首字母轉換成大寫。當 `conf` 上的第 2、3、4 位為 1 時(conf 等於 14)將會執行上述操作。在內部我們使用 if 語句來取出這些位並且根據相應的配置操作字串。## `^` 操作符XOR 操作符在 Go 中用 `^` 表示。XOR 是特例化的 OR,它有以下特性:```goGiven operands a, bXOR(a, b) = 1; only if a != b else = 0// 給定 2 個運算元 a,b// 若且唯若 a!=b 時,操作 XOR(a, b) 返回 1。// 否者返回 0。```這就暗示了我們可以使用 XOR 來切換指定位上的值。例如,給定一個 16 位的值,我們可以使用下面的代碼來切換它的前八位: ```go func main() { var a uint16 = 0xCEFF a ^= 0xFF00 // same a = a ^ 0xFF00}// a = 0xCEFF (11001110 11111111)// a ^=0xFF00 (00110001 11111111) ``` 在之前的代碼中,位的值通過 XOR 操作在 0 和 1 之間切換。XOR 的一個實際的用途就是比較兩個數字加號或減號是否相同。兩個數字 `a`、`b`,如果 `(a ^ b) >= 0` 那麼 a 和 b 同號,如果 `(a ^ b) < 0` 那麼 a 和 b 異號。```gofunc main() { a, b := -12, 25 fmt.Println("a and b have same sign?", (a ^ b) >= 0)}```[線上運行](https://play.golang.org/p/6rAPti5bXJ) 當上述代碼執行會輸出 `a and b have same sign? false`。使用 Go Playground 可以修改不同的符號查看不同的結果。### 使用 `^` 作為位非操作 不像其它語言(C/C++、Java、Python、Javascript 等),Go 沒有一元運算子。XOR 操作符 `^` 可以作為一元操作符來計算一個數位補碼。給定一個位 `x`,在 Go 中 `^x = 1 ^ x` 將會反轉 x 的位。我們可以通過 `^a` 來計算變數 `a` 的補碼。```gofunc main() { var a byte = 0x0F fmt.Printf("%08b\n", a) fmt.Printf("%08b\n", ^a)}// prints00001111 // var a11110000 // ^a```[線上運行](https://play.golang.org/p/5d1fQjDAIv)### `&^` 運算子`&^` 運算子叫做 AND NOT。它是一個 使用 `AND` 後,再使用 `NOT` 操作的簡寫。該操作符定義如下:```Given operands a,bAND_NOT(a, b) = AND(a, NOT(b))// 給定兩個運算元 a,b// 當 a=NOT(b)=1 時,操作 AND_NOT(a, b) 返回 1。// 否則返回 0。```它有一個有意思的特性:如果第二個操作符返回 1。那麼該位將會被清 0。```AND_NOT(a, 1) = 0; clears aAND_NOT(a, 0) = a; ```下面這個程式碼片段使用 AND NOT 操作符來清掉 `a` 的後 4 位(`1010 1011` 到 `1010 0000`)。```gofunc main() { var a byte = 0xAB fmt.Printf("%08b\n", a) a &^= 0x0F fmt.Printf("%08b\n", a)}// prints:1010101110100000```[線上運行](https://play.golang.org/p/UPUlBOPRGh)## `<<` 和 `>>` 運算子和其它 C 家族語言一樣,Go 使用 `<<` 和 `>>` 來代表左移或者右移運算,定義如下:```Given integer operands a and n,a << n; 將 a 中的所有位向左位移 n 次a >> n; 將 a 中的所有位向右位移 n 次```例如:在下面的片段中,`a` 使用左移運算子(`00000011`)左移 3 次。每次的結果都會被列印出來。```gofunc main() { var a int8 = 3 fmt.Printf("%08b\n", a) fmt.Printf("%08b\n", a<<1) fmt.Printf("%08b\n", a<<2) fmt.Printf("%08b\n", a<<3)}// prints:00000011000001100000110000011000```需要注意的是,每次左移右邊的位都會被 0 填充。相反的,使用右移運算子時左邊的位都會被 0 填充(有符號數字除外,具體請看之後的 *Arithmetic Shifts* 章節)。```gofunc main() { var a uint8 = 120 fmt.Printf("%08b\n", a) fmt.Printf("%08b\n", a>>1) fmt.Printf("%08b\n", a>>2)}// prints:011110000011110000011110```一些簡單的左移和右移的提示就是乘法和除法,其中每次的位移都是乘或者除 2 次冪。下面的代碼將 200 除以 2。```gofunc main() { a := 200 fmt.Printf("%d\n", a>>1)}// prints:100```[線上運行](https://play.golang.org/p/EJi0YCARun)或者將一個值乘 4: ```go func main() { a := 12 fmt.Printf("%d\n", a<<2)}// prints:48```[線上運行](https://play.golang.org/p/xuJRcKgMVV)位移運算子提供一種非常有趣的方式來設定一個二進位的值。我們使用 `|` 和 `<<` 來設定 `a` 第三位的值。```gofunc main() { var a int8 = 8 fmt.Printf("%08b\n", a) a = a | (1<<2) fmt.Printf("%08b\n", a)}// prints:0000100000001100```[線上運行](https://play.golang.org/p/h7WoP7ieuI)或者使用 `&` 和位移運算子來測試第 n 位是不是指定的值:```gofunc main() { var a int8 = 12 if a&(1<<2) != 0 { fmt.Println("take action") }}// prints:take action```[線上運行](https://play.golang.org/p/Ptc7Txk5Jb)使用 `&^` 和位移運算來給第 n 位置 0:```gofunc main() { var a int8 = 13 fmt.Printf("%04b\n", a) a = a &^ (1 << 2) fmt.Printf("%04b\n", a)}// prints:11011001```[線上運行](https://play.golang.org/p/Stjq9oOjKz)### 位移運算子注意事項當移動的是一個有符號的值,Go 將會自動的適配位移運算。在向右位移時,正負位上的值將會填充在缺失的位上。

via: https://medium.com/learning-the-go-programming-language/bit-hacking-with-go-e0acee258827

作者:Vladimir Vivien 譯者:saberuster 校對:rxcai

本文由 GCTT 原創編譯,Go語言中文網 榮譽推出

本文由 GCTT 原創翻譯,Go語言中文網 首發。也想加入譯者行列,為開源做一些自己的貢獻嗎?歡迎加入 GCTT!
翻譯工作和譯文發表僅用於學習和交流目的,翻譯工作遵照 CC-BY-NC-SA 協議規定,如果我們的工作有侵犯到您的權益,請及時聯絡我們。
歡迎遵照 CC-BY-NC-SA 協議規定 轉載,敬請在本文中標註並保留原文/譯文連結和作者/譯者等資訊。
文章僅代表作者的知識和看法,如有不同觀點,請樓下排隊吐槽

725 次點擊  
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.