ホームページ > バックエンド開発 > Golang > Golang のビット演算 (ビット演算子) の簡単な分析

Golang のビット演算 (ビット演算子) の簡単な分析

青灯夜游
リリース: 2023-02-24 19:42:25
転載
4486 人が閲覧しました

Golang のビット演算 (ビット演算子) の簡単な分析

コンピュータのメモリが高価で処理能力が限られていた古き良き時代には、ハッカースタイルのビット演算を使用して情報を処理することが好まれていた方法でした (場合によってはそれが唯一の方法でした)やれ)。今日に至るまで、ビット演算の直接使用は、低レベルのシステム プログラミング、グラフィックス処理、暗号化など、多くのコンピューティング分野で不可欠な部分となっています。

Go プログラミング言語は、次のビット単位の演算子をサポートしています。

&   bitwise AND
 |   bitwise OR
 ^   bitwise XOR
&^   AND NOT
<<   left shift
>>   right shift
ログイン後にコピー

この記事の残りの部分では、各演算子とその使用例について詳しく説明します。

& 演算子

Go では、& 演算子は、2 つの整数オペランド ## 演算の間でビットごとの AND# を実行します。 AND 演算には次のプロパティがあります。

Given operands a, b
AND(a, b) = 1; only if a = b = 1
               else = 0
ログイン後にコピー

AND この演算子には、整数データのビットを選択的に 0 にクリアするという優れた効果があります。たとえば、& 演算子を使用して、最後の 4 つの最下位ビット (LSB) をすべて 0 にクリア (設定) できます。

func main() {
    var x uint8 = 0xAC    // x = 10101100
    x = x & 0xF0          // x = 10100000
}
ログイン後にコピー

すべてのビット演算は、省略された代入形式をサポートしています。たとえば、前の例は次のように書き換えることができます。

func main() {
    var x uint8 = 0xAC    // x = 10101100
    x &= 0xF0             // x = 10100000
}
ログイン後にコピー

もう 1 つの巧妙なトリックは、

& 演算を使用して、数値が奇数か偶数かをテストできることです。その理由は、数値の最下位ビットが 1 の場合、それは奇数であるためです。数値と 1 を使用して & 演算を実行し、次に AND 演算を 1 で実行できます。結果が 1 の場合、元の数値は奇数です

import (
    "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)
        }
    }
}
ログイン後にコピー

上記の例を

Playground

で実行します| Operator

| は整数オペランドで演算します。ビット単位の または 演算。 または 演算子には次のプロパティがあることを思い出してください。

Given operands a, b
OR(a, b) = 1; when a = 1 or b = 1
              else = 0
ログイン後にコピー

ビットごとの

または 演算子を使用して、特定の整数の個々のビットを選択的に設定できます。たとえば、次の例では、ビット単位の または を使用して、例の番号の 3 番目、7 番目、および 8 番目のビット (下位ビットから上位ビット (MSB) まで) を 1 に設定します。

func main() {
    var a uint8 = 0
    a |= 196
    fmt.Printf("%b", a)
}

// 打印结果  11000100
            ^^   ^
ログイン後にコピー

例はゴルフ練習場で実行できます

または 演算は、ビット マスキング手法を使用して特定の整数に任意のビットを設定する場合に便利です。たとえば、前のプログラムを拡張して、変数 a に格納されている値により多くのビットを設定できます。

func main() {
    var a uint8 = 0
    a |= 196
    a |= 3
    fmt.Printf("%b", a)
}

// 打印结果 11000111
ログイン後にコピー

サンプルは、

練習フィールドで実行できます。

前のプログラムでは、10 進数の 196 をビットごとに設定する必要があるだけでなく、下位ビットの 10 進数の 3 も設定する必要があります。続行して (

または にさらに値を追加)、すべてのビットを設定します。

ビット演算の構成の使用法

ここで、

AND(a, 1) = a の場合と、a = 1 の場合のみを確認します。 。この機能を使用して、設定ビットの値を問い合わせることができます。たとえば、上記のコード a & 196 は、これらのビットの値が a に存在するため、196 を返します。したがって、ORAND 演算を組み合わせて使用​​して、特定のビットの構成値をそれぞれ設定および読み取ることができます。 .

次のソース コード スニペットは、この操作を示しています。関数

procstr は文字列の内容を変換します。 2 つのパラメータが必要です: 1 つ目の str は変換する文字列で、2 つ目の conf は複数の変換構成を指定するビット マスクです。

const (
    UPPER  = 1 // 大写字符串
    LOWER  = 2 // 小写字符串
    CAP    = 4 // 字符串单词首字母大写
    REV    = 8 // 反转字符串
)

func main() {
    fmt.Println(procstr("HELLO PEOPLE!", LOWER|REV|CAP))
}

func procstr(str string, conf byte) 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)
    }

    // 查询配置中的位操作
    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
}
ログイン後にコピー

Playground.

上記の

procstr("HELLO PEOPLE!", LOWER|REV|CAP) メソッドが変更されます。文字列を小文字に変換し、次に文字列を反転して、最後に文字列内の単語の最初の文字を大文字にします。この機能は、conf の 2 桁目、3 桁目、4 桁目を 14 に設定することで実現されます。次に、コードは連続する if ステートメント ブロックを使用してこれらのビット操作を取得し、対応する文字列変換を実行します。

#^ 演算子

#Go では、ビットごとの #XOR

演算は

# で表されます。 XOR 演算子には次の特性があります。

Given operands a, b
XOR(a, b) = 1; only if a != b
     else = 0
ログイン後にコピー
XOR 演算

のこの特性は、バイナリ ビットの 1 つの値を別の値に変更するために使用できます。たとえば、16 進値が与えられた場合、次のコードを使用して、値の最初の 8 ビット (MSB から開始) を切り替えることができます。

func main() {
    var a uint16 = 0xCEFF
    a ^= 0xFF00 // same a = a ^ 0xFF00
}

// a = 0xCEFF   (11001110 11111111)
// a ^=0xFF00   (00110001 11111111)
ログイン後にコピー

在前面的代码片段中,与 1 进行异或的位被翻转(从 0 到 1 或从 1 到 0)。异或 运算的一个实际用途,例如,可以利用 异或运算去比较两个数字的符号是否一样。当 (a ^ b) ≥ 0 (或相反符号的 (a ^ b) < 0 )为 true 的时候,两个整数 a,b 具有相同的符号,如下面的程序所示:

func main() {
    a, b := -12, 25
    fmt.Println("a and b have same sign?", (a ^ b) >= 0)
}
ログイン後にコピー

在 Go 的 Playground运行代码。

当执行上面这个程序的时候,将会打印出:a and b have same sign? false。在 Go Playground 上修改程序里 a ,b 的符号,以便看到不同的结果。

^ 作为取反位运算符 (非)

不像其他语言 (c/c++,Java,Python,Javascript,等), Go 没有专门的一元取反位运算符。取而代之的是,XOR 运算符 ^,也可作为一元取反运算符作用于一个数字。对于给定位 x,在 Go 中 x = 1 ^ x 可以翻转该位。在以下的代码段中我们可以看到使用 ^a 获取变量 a 的取反值的操作。

func main() {
    var a byte = 0x0F
    fmt.Printf("%08b\n", a)
    fmt.Printf("%08b\n", ^a)
}

// 打印结果
00001111     // var a
11110000     // ^a
ログイン後にコピー

练习场中可以运行范例。

&^ 操作符

&^ 操作符意为 与非,是 操作符的简写形式,它们定义如下。

Given operands a, b
AND_NOT(a, b) = AND(a, NOT(b))
ログイン後にコピー

如果第二个操作数为 1 那么它则具有清除第一个操作数中的位的趣味特性。

AND_NOT(a, 1) = 0; clears a
AND_NOT(a, 0) = a;
ログイン後にコピー

接下来的代码片段使用 AND NOT 操作符,将变量值1010 1011变为 1010 0000,清除了操作数上的低四位。

func main() {
    var a byte = 0xAB
     fmt.Printf("%08b\n", a)
     a &^= 0x0F
     fmt.Printf("%08b\n", a)
}

// 打印:
10101011
10100000
ログイン後にコピー

练习场中运行范例。

<< 和 >> 操作符

与其他 C 的衍生语言类似, Go 使用 << 来表示左移运算符和右移运算符,如下所示:

Given integer operands a and n,
a << n; shifts all bits in a to the left n times
a >> n; shifts all bits in a to the right n times
ログイン後にコピー

例如,在下面的代码片段中变量 a00000011)的值将会左移位运算符分别移动三次。每次输出结果都是为了说明左移的目的。

func 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)
}

// 输出的结果:
00000011
00000110
00001100
00011000
ログイン後にコピー

Playground 运行代码

注意每次移动都会将低位右侧补零。相对应,使用右移位操作符进行运算时,每个位均向右方移动,空出的高位补零,如下示例 (有符号数除外,参考下面的算术移位注释)。

func main() {
 var a uint8 = 120
 fmt.Printf("%08b\n", a)
 fmt.Printf("%08b\n", a>>1)
 fmt.Printf("%08b\n", a>>2)
}

// 打印:
01111000
00111100
00011110
ログイン後にコピー

练习场中可以运行范例。

可以利用左移和右移运算中,每次移动都表示一个数的 2 次幂这个特性,来作为某些乘法和除法运算的小技巧。例如,如下代码中,我们可以使用右移运算将 200(存储在变量 a 中)除以 2 。

func main() {
    a := 200
    fmt.Printf("%d\n", a>>1)
}

// 打印:
100
ログイン後にコピー

练习场 中可以运行范例。

或是通过左移 2 位,将一个数乘以4:

func main() {
    a := 12
    fmt.Printf("%d\n", a<<2)
}
// 打印:

48
ログイン後にコピー

练习场 中可以运行范例。

位移运算符提供了有趣的方式处理二进制值中特定位置的值。例如,下列的代码中,|<< 用于设置变量 a 的第三个 bit 位。

func main() {
    var a int8 = 8
    fmt.Printf("%08b\n", a)
    a = a | (1<<2)
    fmt.Printf("%08b\n", a)
}
// prints:
00001000
00001100
ログイン後にコピー

可以在 练习场 中运行代码示例。

或者,您可以组合位移运算符和 & 测试是否设置了第n位,如下面示例所示:

func main() {
    var a int8 = 12
    if a&(1<<2) != 0 {
        fmt.Println("take action")
    }
}

// 打印:
take action
ログイン後にコピー

练习场中运行代码。

使用 &^ 和位移运算符,我们可以取消设置一个值的某个位。例如,下面的示例将变量 a 的第三位置为 0 :

func main() {
    var a int8 = 13 
    fmt.Printf("%04b\n", a)
    a = a &^ (1 << 2)
    fmt.Printf("%04b\n", a)
}

// 打印:
1101
1001
ログイン後にコピー

练习场 中运行代码。

关于算术位移运算的笔记

当要位移的值(左操作数)是有符号值时,Go 自动应用算术位移。在右移操作期间,复制(或扩展)二进制补码符号位以填充位移的空隙。

概要

他の最新の演算子と同様に、Go はすべてのバイナリ ビット操作演算子をサポートしています。この記事では、これらの演算子を使用して実行できるさまざまなハックの例を紹介するだけです。ウェブ上には多くの記事、特に Sean Eron Anderson が書いた Bit Twiddle Hacks が見つかります。

Twitter で Vladim @vladimirvivien をフォローしてください。

Go を学習している場合は、Vladimir Vivien の Go に関する書籍 Learning Go Programming を読んでください。

この記事は元々、著者の Vladimir Vivien によって Medium で Bit Hacking with Go として公開されたものです。

元のアドレス: https://dev.to/vladimirvivien/bit-hacking-with-go

翻訳アドレス: https://learnku.com/go/t/ 23460/bit-operation-of-go

推奨学習: Golang チュートリアル

以上がGolang のビット演算 (ビット演算子) の簡単な分析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ソース:learnku.com
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート