Pythonのビット演算子(論理積、論理和、排他的論理和、反転、シフト)
Pythonには&
, |
, ^
, ~
, <<
, >>
のビット演算子が用意されており、2進数で表した整数型int
の値の各ビットに対して、それぞれ論理積、論理和、排他的論理和、ビット反転、左ビットシフト、右ビットシフトを行う。
ここでは、
- 論理積(AND):
&
演算子 - 論理和(OR):
|
演算子 - 排他的論理和(XOR):
^
演算子
について説明してから、
- 負の整数に対するビット演算
および、
- ビット反転(NOT):
~
演算子 - ビットシフト:
<<
演算子、>>
演算子
について説明する。
なお、整数を2進数、8進数、16進数で記述する方法や、bin()
, oct()
, hex()
やformat()
を使った2進数、8進数、16進数の数値、文字列の変換については以下の記事を参照。
また、ビット単位の演算ではなく、真偽値bool
型(True
, False
)に対する論理演算(ブール演算)については以下の記事を参照。&
, |
ではなく、and
, or
を使う。
論理積(AND): &演算子
&
演算子による論理積(AND)の例。bin()
で2進数表記の文字列に変換した結果を合わせて出力している。
x = 9 # 0b1001
y = 10 # 0b1010
print(x & y)
print(bin(x & y))
# 8
# 0b1000
論理和(OR): |演算子
|
演算子による論理積(OR)の例。bin()
で2進数表記の文字列に変換した結果を合わせて出力している。
print(x | y)
print(bin(x | y))
# 11
# 0b1011
排他的論理和(XOR): ^演算子
^
演算子による論理積(XOR)の例。bin()
で2進数表記の文字列に変換した結果を合わせて出力している。
print(x ^ y)
print(bin(x ^ y))
# 3
# 0b11
論理積(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 |
負の整数に対するビット演算
負の整数に対してビット演算を行うと、値が2の補数形式で表現されているものとして処理される。
ただし、負の整数をbin()
やformat()
などで2進数の文字列に変換すると、2の補数形式ではなく絶対値にマイナス符号が付いた形になるので注意が必要。
2の補数表現の文字列を取得したい場合は、4bitなら0b1111
(=0xf
)、8bitなら0xff
、16bitなら0xffff
のように、必要なビット桁数の最大値とのANDを取る。
2の補数表現(各ビットを反転させて1
を加える)の文字列が取得できる。
x = -9
print(x)
print(bin(x))
# -9
# -0b1001
print(bin(x & 0xff))
print(format(x & 0xffff, 'x'))
# 0b11110111
# fff7
ビット反転: ~演算子
~
演算子によるビット反転の例。
ビット反転は、単純に各ビットを反転した値ではなく、~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の補数表現の文字列で考えたほうがいい。