Go 言語(以下 Golang)で、バイトデータの「特定箇所のビット」を確実に
0
にしたい。
0b10000100
→ (マスク:0b100
) →0b10000000
0b10000000
→ (マスク:0b100
) →0b10000000
0b11111111
→ (マスク:0b100
) →0b11111011
つまり、スイッチを下げるがごとく、ビットマスクが 1
なら、どのような状態であっても 0
にしたいのです。
いわゆるひとつの、論理演算における「非含意」(P ⊅ Q
)をしたいのです(ゆうて、そんな名前が付いてるって知らんかったけど)。
TL; DR (今北産業)
- やりたいことは以下。
- 具体的には
&^
を使う。例えばP&^Q
- 一見、
&^
が非含意演算子に見えますが、P & (^Q)
のことです。上記、等価式のP&¬Q
はP AND (XOR Q)
という意味です
- 一見、
- ビットマスクが
1
なら、どんな状態でも0
にする例-
0b10000000 & ^0b100 = 0b10000000
(P & ¬Q
) -
0b10000100 & ^0b100 = 0b10000000
(P & ¬Q
) -
0b11111111 & ^0b100 = 0b11111011
(P & ¬Q
)
-
TL; DR (Golang のサンプル・コード)
1
を 0
に反転させたいからと、単純にマスクを XOR 演算(^
、排他的論理和)して反転させるも、2 回適用すると 1
に戻ってしまいます。
- ビットマスク
0b100
で 2 回 XOR する例-
0b10000000 ^ 0b100 = 0b10000100
(P XOR Q = R
) -
0b10000100 ^ 0b100 = 0b10000000
(R XOR Q = P
)
-
... ... まぁ、「反転」なので当然です。一般的には「反転させたい値と同じ長さの逆のマスク」を AND 演算(&
)します。
- ビットマスク
0b11111011
で 2 回 AND する例-
0b10000000 & 0b11111011 = 0b10000000
(P AND Q = R
) -
0b10000100 & 0b11111011 = 0b10000000
(R AND Q = R
)
-
問題は、ビットマスクを 0b100
の形で指定したいのです。
「それならば、マスクを ^
(XOR
)してから、&
すればいいじゃない」と、A & (^B)
と B を反転させてから XOR
すればいいだけでした。
そして、処理優先度が ^
の方が &
より上(^ > &
)なので A&^B
(= A & ^B
) で落ち着きました。あっちょんぶりけ。
package main
import "fmt"
func Example() {
const (
input1 = 0b10000000
input2 = 0b10001000
mask = 0b00001000
)
// AND
fmt.Printf("%08b AND %08b = %08b\n", input1, mask, input1&mask)
fmt.Printf("%08b AND %08b = %08b\n", input2, mask, input2&mask)
// XOR
fmt.Printf("%08b XOR %08b = %08b\n", input1, mask, input1^mask)
fmt.Printf("%08b XOR %08b = %08b\n", input2, mask, input2^mask)
// AND XOR
fmt.Printf("%08b AND XOR %08b = %08b\n", input1, mask, input1&^mask)
fmt.Printf("%08b AND XOR %08b = %08b\n", input2, mask, input2&^mask)
// 右から 4 ビット目の動きに注目
//
// Output:
// 10000000 AND 00001000 = 00000000
// 10001000 AND 00001000 = 00001000
// 10000000 XOR 00001000 = 10001000
// 10001000 XOR 00001000 = 10000000
// 10000000 AND XOR 00001000 = 10000000
// 10001000 AND XOR 00001000 = 10000000
}
- オンラインで動作をみる @ GoPlayground