not演算子を使って整数の0と1を反転する方法と、~演算子を使ってビット幅で0と1を反転する方法を紹介する。
# 整数の0と1を反転する
def invert_0_and_1(x):
return int(not x)
result = invert_0_and_1(0)
print(result) # 1
result = invert_0_and_1(1)
print(result) # 0
result = invert_0_and_1(2) # 0以外の値は0になる
print(result) # 0
# ビット単位に0と1を反転させる
from math import log2, ceil
def invert_bitwise(x):
if x == 0:
d = 1
else:
d = ceil(log2(abs(x))) + 1 # ビット幅を求める
mask = (1 << d) - 1
tmp = ~x
orig_repr = f'{x & mask:0= {d+1}b}'
inverted_repr = f'{tmp & mask:0= {d+1}b}'
#print(f'{orig_repr}\n{inverted_repr}')
return orig_repr, inverted_repr
o, i = invert_bitwise(-128)
print(f'{o}\n{i}')
# 出力結果:
# 10000000
# 01111111
o, i = invert_bitwise(0)
print(f'{o}\n{i}')
# 出力結果:
# 0
# 1
o, i = invert_bitwise(5)
print(f'{o}\n{i}')
# 出力結果:
# 0101
# 1010
整数の0と1を反転するには幾つかの方法が考えられる。例えば、if文を使ってもよいだろうし、ビット演算子「^」を使って1と排他的論理和を取る方法も考えられる(「0 ^ 1」は「1」に、「1 ^ 1」は0になる)。あるいはPythonでは0は偽値として1は真値として扱われる(逆に偽値は0として真値は1として扱われる)特性を利用して、以下のようなコードを書くことも考えられる。
def invert_0_and_1(x):
return int(not x)
この例ではinvert_0_and_1関数は受け取った値を論理値として扱い、真偽を反転させ、それを整数値に変換している。以下は実行例だ。
result = invert_0_and_1(0)
print(result) # 1
result = invert_0_and_1(1)
print(result) # 0
このように0と1が反転していることが分かる。なお、Pythonでは0以外の値は真値として扱われるので、以下のように0以外の整数をこの関数に渡すと0が返される。
result = invert_0_and_1(2) # 0以外の値は0になる
print(result) # 0
整数の0と1を反転させるのではなく、ビット単位に0と1を反転させるにはビット演算子の「~」が使用できる。
x = 0b1101 # 10進数の13
print(x) # 13
y = ~x
これによりビット単位で0と1が反転される。ただし、その値を単純にbin関数で調べてもビット単位で反転しているようには見えない。これはbin関数に負値を渡しても、その絶対値が2進数として表記され、符号はその前に置かれるようになっているからだ。
print(y) # -14
print(bin(y)) # -0b1110
変数xの値である0b1101(10進数で13)のビットを反転させると、0b0010のようになってほしいがそうはなっていないように見えてしまう(Pythonのドキュメントではビット反転では「x のビット単位反転は、-(x+1) として定義されて」いると述べられている。このため、13をビット反転した値は-14となっている)。
だが、このyの値と2進数のマスク0b11111111とANDを取ってみると(-14の2進表現で各ビットが1になっている桁だけが1となる)、次のような結果が得られる。
print(f'{y & 0b11111111:08b}') # # 11110010
この結果である「11110010」の下位の4桁に注目すると、元の「0b1101」(10進数)の各ビットがちゃんと反転されていることが確認できる。
なお、「11110010」は-14を2の補数表現で表記したものであり、その値を10進数に直すには1と0を反転させて1を加算する(符号は最上位ビットが1なのでマイナス)。0と1を反転させると「00001101」となり、そこに1を加算した値である「00001110」を10進数にすると「8+4+2」=14、符号は既に述べた通りマイナスなので、「11110010」は10進数では「-14」であると計算できる。
このことを使用して、ここではビット単位に0と1を反転させた結果を得る次のような関数を定義した。
from math import log2, ceil
def invert_bitwise(x):
if x == 0:
d = 1
else:
d = ceil(log2(abs(x))) + 1 # ビット幅を求める
mask = (1 << d) - 1
tmp = ~x
orig_repr = f'{x & mask:0= {d+1}b}'
inverted_repr = f'{tmp & mask:0= {d+1}b}'
print(f'{orig_repr}\n{inverted_repr}')
return orig_repr, inverted_repr
最初にあるのは、関数が受け取った整数値を2進数で表現するのに必要な桁数を計算して変数dに代入するコードだ。変数maskには上で見たようなAND演算を行うためのマスク(全てのビットが1で桁数は上で計算したもの)を設定している。その後、~演算子でビットを反転した結果を得た後に、元の値とビット反転した値のビットパターンを文字列化している。
実行例を以下に示す。
o, i = invert_bitwise(-128)
print(f'{o}\n{i}')
# 出力結果:
# 10000000
# 01111111
o, i = invert_bitwise(0)
print(f'{o}\n{i}')
# 出力結果:
# 0
# 1
o, i = invert_bitwise(5)
print(f'{o}\n{i}')
# 出力結果:
# 0101
# 1010
Copyright© Digital Advantage Corp. All Rights Reserved.