前置き
最近 Go 1.22 がリリースされたのでこの機会にリリース内容を理解したいとチャレンジしており、言語の変更の中に「for」ループのバグに対する修正がありました。
詳細は、Go Wiki: LoopvarExperimentに記載されています。上から読んでいて、次のようなループの例が登場しました。
func TestAllEvenBuggy(t *testing.T) {
testCases := []int{1, 2, 4, 6}
for _, v := range testCases {
t.Run("sub", func(t *testing.T) {
t.Parallel()
if v&1 != 0 {
t.Fatal("odd v", v)
}
})
}
}
その中で v&1
という記述があり、初めてみた記述だったのでAIのClaudeに聞いてみたところ「ビット演算」を行っているとのことでした。
ビット演算について調べてみると2進数のリテラル同士の例が多く、「10進数のリテラル同士のビット演算をすると返ってくる結果は10進数のリテラルが返ってくるの?」や「10進数のリテラル同士や2進数のリテラル同士をビット演算することはできるの?」など疑問に思った部分について調べてみました。
ビット演算やGoの仕様については私の理解が浅く、的確でない質問や説明になってしまう可能性があります。ご了承ください。
要約
- 10進数のリテラル同士のビット演算をすると返ってくる結果は10進数のリテラルが返ってくるの?
- 実際に試して10進数のリテラルのときは10進数のリテラルが返ってきました
- 10進数のリテラル同士や2進数のリテラル同士をビット演算することはできるの?
- ビット演算子は、整数に適用することができる
- リテラルの表記方法は関係なく、整数型の値同士であればビット演算をできるようです。
- ビット演算子は、整数に適用することができる
v&1
を実行するとどのような結果になる?
登場したコードを簡略化したものです。
&
の左辺には1,2,4,6が順に渡されます。&
の右辺は固定で1です。
func main() {
nums := []int{1, 2, 4, 6}
for _, v := range nums {
fmt.Println(v & 1)
}
}
// 結果
// 1&1 => 1
// 2&1 => 0
// 4&1 => 0
// 6&1 => 0
結果は整数の1か0が返ってくるようでした。
&
は ビット演算でいうところの「論理積」を求めるようです。
イメージ的には次のようなことをやっているみたいです。
// 1&1 => 1
0001 = 1
0001 = 1
————
0001 = 1
// 2&1 => 0
0010 = 2
0001 = 1
————
0000 = 0
// 4&1 => 0
0100 = 4
0001 = 1
————
0000 = 0
// 6&1 => 0
0110 = 6
0001 = 1
————
0000 = 0
ビット演算について言語仕様だとどのあたりに書いてある?
算術演算子(Arithmetic operators)の項目に書いてありました。
Arithmetic operators apply to numeric values and yield a result of the same type as the first operand. The four standard arithmetic operators (+, -, *, /) apply to integer, floating-point, and complex types; + also applies to strings. The bitwise logical and shift operators apply to integers only.
算術演算子は数値に適用され、最初のオペランドと同じ型の結果を返す。四則演算子(+、-、*、/)は整数型、浮動小数点型、複素数型に適用され、+は文字列にも適用される。ビットごとの論理演算子およびシフト演算子は、整数のみに適用されます。
& bitwise AND integers
最後に
こういう小さな疑問も深ぼってみると言語仕様に書いてあってGoは面白いですね。
ちなみに今回のループ例のビット演算では奇数の場合は 1
、偶数の場合は 0
を返すので奇数か偶数かを判定するのに使われているみたいでした。