Pythonのビット演算子(論理積、論理和、排他的論理和、反転、シフト)
Pythonにはビット演算子として&
, |
, ^
, ~
, <<
, >>
が用意されている。2進数で表した整数int
の各ビットに対して、それぞれ論理積、論理和、排他的論理和、ビット反転、左ビットシフト、右ビットシフトを行う。
整数を2進数、8進数、16進数で記述する方法や、bin()
, oct()
, hex()
やformat()
を使った2進数、8進数、16進数の数値、文字列の変換については以下の記事を参照。
整数int
の2進数表記における1
の数をカウントする方法については以下の記事を参照。
また、ビット単位の演算ではなく、真偽値bool
型(True
, False
)に対する論理演算(ブール演算)については以下の記事を参照。&
, |
ではなく、and
, or
を使う。
論理積(AND)、論理和(OR)、排他的論理和(XOR)の入出力
論理積(AND)、論理和(OR)、排他的論理和(XOR)の各ビットに対する入力と出力の関係は以下の表の通り。
入力1 | 入力2 | 論理積(AND) | 論理和(OR) | 排他的論理和(XOR) |
---|---|---|---|---|
1 | 1 | 1 | 1 | 0 |
1 | 0 | 0 | 1 | 1 |
0 | 1 | 0 | 1 | 1 |
0 | 0 | 0 | 0 | 0 |
ビット単位論理積(bitwise AND): &演算子
&
演算子によるビット単位論理積(bitwise AND)の例。bin()
で2進数表記の文字列に変換した結果を合わせて出力している。
x = 12 # 0b1100
y = 10 # 0b1010
print(x & y)
print(bin(x & y))
# 8
# 0b1000
ビット単位論理和(bitwise OR): |演算子
|
演算子によるビット単位論理和(bitwise OR)の例。bin()
で2進数表記の文字列に変換した結果を合わせて出力している。
x = 12 # 0b1100
y = 10 # 0b1010
print(x | y)
print(bin(x | y))
# 14
# 0b1110
ビット単位排他的論理和(bitwise XOR): ^演算子
^
演算子によるビット単位排他的論理和(bitwise XOR)の例。bin()
で2進数表記の文字列に変換した結果を合わせて出力している。
x = 12 # 0b1100
y = 10 # 0b1010
print(x ^ y)
print(bin(x ^ y))
# 6
# 0b110
負の整数に対するビット演算
負の整数に対してビット演算を行うと、値が2の補数形式で表現されているものとして処理される。
ただし、負の整数をbin()
やformat()
などで2進数の文字列に変換すると、2の補数形式ではなく絶対値にマイナス符号が付いた形になる。
x = -9
print(x)
print(bin(x))
# -9
# -0b1001
2の補数表現の文字列を取得したい場合は、4bitなら0b1111
(=0xF
)、8bitなら0xFF
、16bitなら0xFFFF
のように、必要なビット桁数の最大値とのANDを取る。
2の補数表現(各ビットを反転させて1
を加える)の文字列が取得できる。
print(bin(x & 0xFF))
print(format(x & 0xFF, 'b'))
# 0b11110111
# 11110111
ゼロ埋めの指定がないと先頭の0
が省略されてしまうので注意。
print(bin(x & 0b1111))
print(format(x & 0b1111, 'b'))
# 0b111
# 111
print(format(x & 0b1111, '#06b'))
print(format(x & 0b1111, '04b'))
# 0b0111
# 0111
ビット反転: ~演算子
~
演算子によるビット反転の例。
Pythonにおけるビット反転は、単純に各ビットを反転した値ではなく、~x
に対して-(x+1)
となる値を返す。
-(x+1)
は入力値x
を2の補数形式とみなして、すべてのビットを反転した値と等価。
上述のように、Pythonでは負の整数をbin()
やformat()
などで2進数の文字列に変換すると、2の補数形式ではなく絶対値にマイナス符号が付いた形になるので、~x
をそのまま文字列に変換しても元の値のビットが反転した文字列にはならない。
x = 9 # 0b1001
print(~x)
print(bin(~x))
# -10
# -0b1010
AND演算を行い、2の補数表現の文字列にすると、元の値のビットが反転していることが分かる。例えば、4桁のビット列をそのまま反転したビット列(符号ビットなし)を取得するには、AND演算を行った値に対してformat()
で'04b'
のようにゼロ埋めする。
print(bin(~x & 0xFF))
print(format(~x & 0b1111, '04b'))
# 0b11110110
# 0110
ビットシフト: <<演算子、>>演算子
ビットシフト演算子<<
, >>
による、左ビットシフト、右ビットシフトの例。
x = 9 # 0b1001
print(x << 1)
print(bin(x << 1))
# 18
# 0b10010
print(x >> 1)
print(bin(x >> 1))
# 4
# 0b100
負の値に対しては符号ビットが拡張されてシフトされ、正負の符号は変わらない。負の値は左側にずっと1
が並んでいるイメージ。
x = -9
print(bin(x))
print(bin(x & 0xFF))
# -0b1001
# 0b11110111
print(x << 1)
print(bin(x << 1))
print(bin((x << 1) & 0xFF))
# -18
# -0b10010
# 0b11101110
print(x >> 1)
print(bin(x >> 1))
print(bin((x >> 1) & 0xFF))
# -5
# -0b101
# 0b11111011
数値で考えるとよく分からなくなるので、2の補数表現の文字列で考えたほうがいい。