ビット演算と使用法


102

このコードを考えてみましょう:

x = 1        # 0001
x << 2       # Shift left 2 bits: 0100
# Result: 4

x | 2        # Bitwise OR: 0011
# Result: 3

x & 1        # Bitwise AND: 0001
# Result: 1

Python(および他の言語)の算術演算子は理解できますが、「ビット単位」演算子についてはよく理解できませんでした。上記の例(Pythonの本から)では、左シフトは理解できますが、他の2つは理解できません。

また、実際に使用されるビット演算子は何ですか?いくつかの例をいただければ幸いです。


11
これは興味深いかもしれません:stackoverflow.com/questions/276706/what-are-bitwise-operators
outis

回答:


163

ビット演算子は、マルチビット値で機能する演算子ですが、概念的には一度に1ビットです。

  • AND入力が両方とも 1の場合のみ1で、それ以外の場合は0です。
  • OR入力の一方または両方が1の場合は1で、それ以外の場合は0です。
  • XOR入力の1つだけが1の場合のみ1で、それ以外の場合は0です。
  • NOT 入力が0の場合のみ1、それ以外の場合は0です。

これらはしばしば真理値表として最もよく示されます。入力の可能性は上と左にあり、結果のビットは入力の交点に表示される4つの値(入力が1つしかないためNOTの場合は2つ)の1つです。

AND | 0 1     OR | 0 1     XOR | 0 1    NOT | 0 1
----+-----    ---+----     ----+----    ----+----
 0  | 0 0      0 | 0 1       0 | 0 1        | 1 0
 1  | 0 1      1 | 1 1       1 | 1 0

1つの例は、整数の下位4ビットのみが必要な場合、15(2進数の1111)とANDするので、次のようになります。

    201: 1100 1001
AND  15: 0000 1111
------------------
 IS   9  0000 1001

その場合の15のゼロビットはフィルターとして効果的に機能し、結果のビットも強制的にゼロにします。

さらに、>>および<<多くの場合、ビット演算子として含まれており、それらの「シフト」は、それぞれ右と最後のロールは、あなたがでゼロビットに向けてシフトし、摂食していることのビットを捨て、一定数のビットによって残された値違った終わり方。

したがって、たとえば:

1001 0101 >> 2 gives 0010 0101
1111 1111 << 4 gives 1111 0000

Pythonの左シフトは、ビットが破棄される固定幅を使用しないという点で珍しいことに注意してください。多くの言語はデータ型に基づいて固定幅を使用しますが、Pythonは幅を拡張して余分なビットに対応します。Pythonで破棄動作を取得するにandは、8ビット値を左に4ビットシフトする場合のように、ビット単位で左シフトを追跡できます。

bits8 = (bits8 << 4) & 255

これを念頭に置いて、ビットごとの演算子の別の例は、2つの4ビット値を8ビット値にパックする場合、3つの演算子(left-shiftandおよびor)をすべて使用できます。

packed_val = ((val1 & 15) << 4) | (val2 & 15)
  • & 15操作により、両方の値の下位4ビットのみが確認されます。
  • << 4移動する左4ビットシフトでありますval1 8ビット値の上位4ビットに。
  • |単にこれら二つを一緒に組み合わせています。

val1が7でval24の場合:

                val1            val2
                ====            ====
 & 15 (and)   xxxx-0111       xxxx-0100  & 15
 << 4 (left)  0111-0000           |
                  |               |
                  +-------+-------+
                          |
| (or)                0111-0100

43

1つの典型的な使用法:

| 特定のビットを1に設定するために使用されます

& 特定のビットをテストまたはクリアするために使用されます

  • ビットを設定します(nはビット番号、0は最下位ビットです)。

    unsigned char a |= (1 << n);

  • 少しクリア:

    unsigned char b &= ~(1 << n);

  • 少し切り替えます:

    unsigned char c ^= (1 << n);

  • 少しテストしてください:

    unsigned char e = d & (1 << n);

たとえば、リストの場合を考えてみます。

x | 2 のビット1を設定するために使用されます x 1〜

x & 1のビット0 xが1または0 かどうかをテストするために使用されます


38

ビットワイズ演算子は実際に何に使用されていますか?いくつかの例をいただければ幸いです。

ビット演算の最も一般的な使用法の1つは、16進数の色を解析することです。

たとえば、次のPython関数は、文字列を受け入れ、#FF09BEそのRed、Green、Blueの値のタプルを返します。

def hexToRgb(value):
    # Convert string to hexadecimal number (base 16)
    num = (int(value.lstrip("#"), 16))

    # Shift 16 bits to the right, and then binary AND to obtain 8 bits representing red
    r = ((num >> 16) & 0xFF)

    # Shift 8 bits to the right, and then binary AND to obtain 8 bits representing green
    g = ((num >> 8) & 0xFF)

    # Simply binary AND to obtain 8 bits representing blue
    b = (num & 0xFF)
    return (r, g, b)

これを達成するためのより効率的な方法があることは知っていますが、これはシフトとビットごとのブール演算の両方を示す本当に簡潔な例だと思います。


14

質問の2番目の部分は:

また、実際に使用されるビット演算子は何ですか?いくつかの例をいただければ幸いです。

部分的にしか対処されていません。これらは、私の問題の2セントです。

プログラミング言語でのビット単位の操作は、多くのアプリケーションを扱うときに基本的な役割を果たします。ほとんどすべての低レベルコンピューティングは、この種の操作を使用して実行する必要があります。

次のような、2つのノード間でデータを送信する必要があるすべてのアプリケーション:

  • コンピューターネットワーク;

  • 電気通信アプリケーション(携帯電話、衛星通信など)。

通信の下位レベルのレイヤーでは、データは通常「フレーム」と呼ばれる形式で送信されます。フレームは、物理チャネルを介して送信されるバイトの文字列です。このフレームには通常、実際のデータと、ヘッダーと呼ばれるものの一部である他のいくつかのフィールド(バイトでコード化)が含まれていますます。ヘッダーには通常、通信のステータス(フラグ(ビット)など)、フレームカウンター、訂正およびエラー検出コードなどに関連するいくつかの情報をエンコードするバイトが含まれています。フレームで送信データを取得し、フレームを使用してデータを送信するには、確実にビットごとの操作が必要です。

一般に、この種のアプリケーションを処理する場合、APIが利用できるため、これらの詳細すべてを処理する必要はありません。たとえば、最近のすべてのプログラミング言語はソケット接続用のライブラリを提供しているため、実際にTCP / IP通信フレームを構築する必要はありません。しかし、これらのAPIをプログラミングしてくれた善良な人々について考えてみてください。彼らは確かにフレームの構築に対処しなければなりませんでした。あらゆる種類のビット単位演算を使用して、低レベルの通信から高レベルの通信に行き来します。

具体的な例として、通信ハードウェアによって直接キャプチャされた生データを含むファイルを誰かがあなたに与えると想像してください。この場合、フレームを見つけるために、ファイル内の生のバイトを読み取り、データをビットごとにスキャンして、ある種の同期ワードを見つける必要があります。同期ワードを特定した後、実際のフレームを取得し、必要に応じてシフトして(そしてそれはストーリーの始まりにすぎません)、送信される実際のデータを取得する必要があります。

別の非常に異なるアプリケーションの低レベルファミリは、パラレルポートやシリアルポートなどのいくつかの(古代のような)ポートを使用してハードウェアを制御する必要がある場合です。このポートは、いくつかのバイトを設定することによって制御され、そのバイトの各ビットは、そのポートに対して指示の点で特定の意味を持っています(たとえば、http://en.wikipedia.org/wiki/Parallel_portを参照)。そのハードウェアで何かを実行するソフトウェアを構築する場合は、ビット単位の演算を実行して、実行する命令をポートが理解できるバイトに変換する必要があります。

たとえば、他のデバイスを制御するためにパラレルポートに接続されている物理ボタンがある場合、これはソフトアプリケーションで見つけることができるコード行です。

read = ((read ^ 0x80) >> 4) & 0x0f; 

これが貢献することを願っています。


特にパラレルポートとシリアルポートについてビットごとの操作が役立つ例として読む場合は、en.wikipedia.org / wiki / Bit_bangingを別の方法として追加してみます。
ダン・

6

これでこれらの2つが明確になることを願っています。

x | 2

0001 //x
0010 //2

0011 //result = 3

x & 1

0001 //x
0001 //1

0001 //result = 1

4
おっと...でも2用のバイナリを知らない馬鹿のようになってしまった:(固定、それは....西部で最速の銃であることを試みた。
Amarghosh

1
x & 1は効果を図示していませんx & 2
dansalmo 2013

5

0を偽、1を真と考えてください。次に、ビット単位のand(&)およびor(|)は、通常のandおよびorと同じように機能し、値のすべてのビットを一度に処理します。設定可能な30のオプション(たとえば、ウィンドウの描画スタイル)がある場合、通常、それらをフラグに使用するのがわかります。それぞれを設定または設定解除するために30の個別のブール値を渡す必要はありません。オプションを1つの値に結合し、&を使用して各オプションが設定されているかどうかを確認します。このフラグ渡しのスタイルは、OpenGLで頻繁に使用されます。各ビットは個別のフラグであるため、2の累乗でフラグ値を取得します(1ビットのみが設定された別名)1(2 ^ 0)2(2 ^ 1)4(2 ^ 2)8(2 ^ 3) 2の累乗は、フラグがオンの場合に設定されているビットを示します。

また、2 = 10なので、x | 2は111(7)ではなく110(6)です(どのビットもオーバーラップしない場合)(この場合はtrue)| 加算のように動作します。


5

上記では見ていませんが、算術演算に左右シフトを使用している人もいます。xによる左シフトは、(オーバーフローしない限り)2 ^ xを掛けることに相当し、右シフトは、2 ^ xで割ることに相当します。

最近、x << 1とx >> 1を2倍にして半分にするのに使用している人を見かけましたが、単に賢くしようとしているのか、それとも通常の演算子よりも明らかに優れているのかはわかりません。


1
私はpythonについては知りませんが、Cなどの低レベルの言語では、アセンブリ、ビット単位のシフトの方がはるかに効率的です。違いを確認するには、Cでプログラムを記述して、それぞれの方法でこれを実行し、アセンブリコードにコンパイルします(または、アセンブリlangを知っている場合は、すでにこれを知っているでしょう:))。命令数の違いを見てください。
0xc0de 2013

2
ビットシフト演算子の使用に対する私の主張は、ほとんどの最新のコンパイラはおそらくすでに算術演算を最適化しているため、賢さはせいぜい最悪か、最悪の場合はコンパイラとの戦いです。私はC、コンパイラ、またはCPUの設計に関する専門知識を持っていないので、私が正しいとは思いません。:)
P.ストールワース2013

これはもっと高いはずです。私はビットワイズ演算子をその方法で正確に使用しているいくつかのコードを処理する必要があり、その答えは物事を理解するのに役立ちました。
Philippe Oger、2018年

4

セット

セットは、数学演算を使用して組み合わせることができます。

  • 共用体演算子|は、2つのセットを組み合わせて、いずれかのアイテムを含む新しいセットを形成します。
  • 交差演算子&は両方でのみアイテムを取得します。
  • 差分演算子-は、最初のセットの項目を取得しますが、2番目の項目は取得しません。
  • 対称差分演算子^は、どちらか一方のセットのアイテムを取得しますが、両方は取得しません。

自分で試す:

first = {1, 2, 3, 4, 5, 6}
second = {4, 5, 6, 7, 8, 9}

print(first | second)

print(first & second)

print(first - second)

print(second - first)

print(first ^ second)

結果:

{1, 2, 3, 4, 5, 6, 7, 8, 9}

{4, 5, 6}

{1, 2, 3}

{8, 9, 7}

{1, 2, 3, 7, 8, 9}

この回答は質問とはまったく関係がなく、他の場所からコピーして貼り付けたようです。
文書化

「ビットワイズ演算子は実際に何に使用されているのですか?」という質問です。この答えは、ビットワイズ演算子のあまり知られていないが非常に有用な使用法を提供します。
テギョン

3

この例では、4つの2ビット値すべての操作を示します。

10 | 12

1010 #decimal 10
1100 #decimal 12

1110 #result = 14

10 & 12

1010 #decimal 10
1100 #decimal 12

1000 #result = 8

次に使用例を示します。

x = raw_input('Enter a number:')
print 'x is %s.' % ('even', 'odd')[x&1]

2

もう1つの一般的な使用例は、ファイルのアクセス許可の操作/テストです。Python statモジュールを参照してください:http : //docs.python.org/library/stat.html

たとえば、ファイルの権限を目的の権限セットと比較するには、次のようにします。

import os
import stat

#Get the actual mode of a file
mode = os.stat('file.txt').st_mode

#File should be a regular file, readable and writable by its owner
#Each permission value has a single 'on' bit.  Use bitwise or to combine 
#them.
desired_mode = stat.S_IFREG|stat.S_IRUSR|stat.S_IWUSR

#check for exact match:
mode == desired_mode
#check for at least one bit matching:
bool(mode & desired_mode)
#check for at least one bit 'on' in one, and not in the other:
bool(mode ^ desired_mode)
#check that all bits from desired_mode are set in mode, but I don't care about 
# other bits.
not bool((mode^desired_mode)&desired_mode)

私は真偽を無視するだけなので、結果をブール値としてキャストしますが、それぞれのbin()値を出力することは価値のある練習になるでしょう。


1
あなたは最後の例では間違っています。これは次のようになります not bool((mode ^ desired_mode) & 0777)。または(より簡単に理解するために): not (mode & 0777) ^ desired_mode == 0。ANDは興味深いビットのみを残し、XORは必要なすべてのビットが設定されているかをチェックします。明示的な== 0比較は、よりも意味がありbool()ます。
Vadim Fint

これはファイル操作に固有のものではないと思います。たとえば、PyQtではに対して同様のことを行いますsetWindowFlags。例:setWindowFlags(SplashScreen | WindowStaysOnTopHint)。あなたが「オン」に設定しているトグルのように見えるので、私はまだこれを混乱させます。
エリック2014

2

整数のビット表現は、ブール演算の配列を反復するよりもビットごとの演算の方がはるかに高速であるため、科学計算で真偽情報の配列を表すためによく使用されます。(高水準言語はビット配列のアイデアを使用するかもしれません。)

これの素晴らしくてかなり単純な例は、Nimのゲームに対する一般的な解決策です。見てください、Pythonの上のコードWikipediaのページを。ビット単位の排他的論理和を多用します^


1

配列要素が2つの値の間にある場所を見つけるより良い方法があるかもしれませんが、この例が示すようにはここで機能しますが、は機能しません。

import numpy as np
a=np.array([1.2, 2.3, 3.4])
np.where((a>2) and (a<3))      
#Result: Value Error
np.where((a>2) & (a<3))
#Result: (array([1]),)

1

私はそれが言及されていなかったので、この例では2ビット値の(-)10進演算を示します:AB(AにBが含まれている場合のみ)

この操作は、プログラムでビットを表す動詞を保持している場合に必要です。場合によってはビットを追加する必要があります(上記のように)、場合によってはビットを削除する必要があります(動詞に含まれている場合)

111 #decimal 7
-
100 #decimal 4
--------------
011 #decimal 3

Pythonの場合: 7&〜4 = 3(7から4を表すビットを削除)

001 #decimal 1
-
100 #decimal 4
--------------
001 #decimal 1

Pythonの場合: 1&〜4 = 1(1から4を表すビットを削除します-この場合、1は4を「含まない」)。


0

整数のビットを操作することは便利ですが、ビットまで指定できるネットワークプロトコルの場合はよくありますが、長いバイトシーケンス(1つの整数に変換しにくい)の操作が必要になる場合があります。この場合、採用することが有用であるビット文字列データにビット演算を可能にするライブラリー-例えば、1つの文字列として、または六角形、ビットシフトとして文字列「ABCDEFGHIJKLMNOPQ」をインポート(または他のビット単位操作を行う)ことができました:

>>> import bitstring
>>> bitstring.BitArray(bytes='ABCDEFGHIJKLMNOPQ') << 4
BitArray('0x142434445464748494a4b4c4d4e4f50510')
>>> bitstring.BitArray(hex='0x4142434445464748494a4b4c4d4e4f5051') << 4
BitArray('0x142434445464748494a4b4c4d4e4f50510')


0

ビットを反転するには(つまり、1の補数/反転)、次の操作を実行できます。

すべて1でExORされた値は反転するため、特定のビット幅に対してExORを使用して反転できます。

In Binary
a=1010 --> this is 0xA or decimal 10
then 
c = 1111 ^ a = 0101 --> this is 0xF or decimal 15
-----------------
In Python
a=10
b=15
c = a ^ b --> 0101
print(bin(c)) # gives '0b101'
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.