こんにちは
Goのstrconvのパッケージを読んでたらintSizeというものがありました。
const intSize = 32 << (^uint(0) >> 63)
intSizeはAtoiメソッド内で使用されています。
if intSize == 32 && (0 < sLen && sLen < 10) || intSize == 64 && (0 < sLen && sLen < 19)
見た感じintSizeには32か64が代入されるであろうということは分かるのですが、なぜその式になるのかが分からなったので調べてみました。
<<
と>>
はビット演算子です。<<
が左シフト演算で>>
が右シフト演算です。
^
はビット演算(XOR)です。
使い方はこんな感じ。
package main import "fmt" const ( A uint8 = 44 ) func main() { a := fmt.Sprintf("2進数: %b, 10進数: %d", A, A) fmt.Println(a) // 2進数: 00101100, 10進数: 44 l := A << 2 b := fmt.Sprintf("2進数: %b, 10進数: %d", l, l) fmt.Println(b) // 2進数: 10110000, 10進数: 176 r := A >> 2 c := fmt.Sprintf("2進数: %b, 10進数: %d", r, r) fmt.Println(c) // 2進数: 1011, 10進数: 11 d := fmt.Sprintf("2進数: %b, 10進数: %d", ^A, ^A) fmt.Println(d) // 2進数: 11010011, 10進数: 211 }
^
は整数演算子としても用意されていて反転の意味になります。44の2進数、00101100
を反転すると11010011
になります。
公式ではこのように書かれています。
^x bitwise complement is m ^ x with m = “all bits set to 1” for unsigned x and m = -1 for signed x
mには毎回1がセットされ、1ビット毎に排他的論理和演算を行っています。
排他的論理和演算は
0 XOR 0 = 0 0 XOR 1 = 1 1 XOR 0 = 1 1 XOR 1 = 0
という性質を持ちます。
先程の^Aでいうと
1 ^ 0 = 1 1 ^ 0 = 1 1 ^ 1 = 0 1 ^ 0 = 1 1 ^ 1 = 0 1 ^ 1 = 0 1 ^ 0 = 1 1 ^ 0 = 1
を内部で行っており、結果11010011
となります。
ここまでは分かれば式で何が行われているのかが分かります。
それでは元のソースコードに戻ります。
const intSize = 32 << (^uint(0) >> 63)
最初に実行されるのは
(^uint(0) >> 63)
の部分でその後に
32 << 上記の計算結果
が実行されます。
uintは環境依存で、32bitか64bitになります。
下記の2つがそれぞれの計算結果になります。
uintが32bitの場合
uint(0) 32bit0埋めにする 0000 0000 0000 0000 0000 0000 0000 0000 ^uint(0) 反転する 1111 1111 1111 1111 1111 1111 1111 1111 ^uint(0) >> 63 右シフト演算で63bitシフトさせる。結果0になる 0000 0000 0000 0000 0000 0000 0000 0000 左シフト演算 32 << 0 = 32 intSize = 32
uintが64bitの場合
uint(0) 64bit0埋めにする 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 ^uint(0) 反転する 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 ^uint(0) >> 63 右シフト演算で63bitシフトさせる。結果1になる 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0001 左シフト演算 32 << 1 = 64 intSize = 64
結果intSizeには32か64が代入されます。
スマート!!
最初見た時はよく分からなかったのですが、調べてみたらとても面白かったです。
引き続きGoのソースリーディングはしていくので、また面白いものがあったらブログに書いていこうと思います。