note.nkmk.me

Pythonのビット演算子(論理積、論理和、排他的論理和、反転、シフト)

Date: 2017-11-28 / tags: Python

Pythonには&, |, ^, ~, <<, >>のビット演算子が用意されており、2進数で表した整数型intの値の各ビットに対して、それぞれ論理積、論理和、排他的論理和、ビット反転、左ビットシフト、右ビットシフトを行う。

ここでは、

  • 論理積(AND): &演算子
  • 論理和(OR): |演算子
  • 排他的論理和(XOR): ^演算子

について説明してから、

  • 負の整数に対するビット演算

および、

  • ビット反転(NOT): ~演算子
  • ビットシフト: <<演算子、>>演算子

について説明する。

なお、整数を2進数、8進数、16進数で記述する方法や、bin(), oct(), hex()format()を使った2進数、8進数、16進数の数値、文字列の変換については以下の記事を参照。

また、ビット単位の演算ではなく、真偽値bool型(True, False)に対する論理演算(ブール演算)については以下の記事を参照。

スポンサーリンク

論理積(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の補数表現の文字列で考えたほうがいい。

スポンサーリンク
シェア
このエントリーをはてなブックマークに追加

関連カテゴリー

関連記事