web-dev-qa-db-ja.com

ビット単位の操作と使用法

次のコードを検討してください。

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つは理解していません。

また、ビット単位演算子は実際に何に使用されますか?私はいくつかの例を感謝します。

92
3zzy

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

  • ANDは、その入力のbothが1の場合のみ1で、それ以外の場合は0です。
  • ORは、その入力の1つまたは両方が1の場合は1、それ以外の場合は0です。
  • XORは、入力のexactly oneが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(バイナリ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は、val1を8ビット値の上位4ビットに移動するための4ビット左シフトです。
  • |は、これら2つを単純に結合します。

val1が7でval2が4の場合:

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

典型的な使用法:

|は、特定のビットを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は、xのビット1を1に設定するために使用されます

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

41
pierrotlefou

ビット単位演算子は実際に何に使用されますか?私はいくつかの例を感謝します。

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

たとえば、#FF09BEのような文字列を受け入れ、その赤、緑、青の値のタプルを返す Python 関数があります。

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)

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

36
bguiz

質問の2番目の部分は:

また、ビット単位演算子は実際に何に使用されますか?私はいくつかの例を感謝します。

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

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

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

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

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

通信の下位層では、データは通常framesと呼ばれるもので送信されます。フレームは、物理チャネルを介して送信される単なるバイトの文字列です。このフレームには通常、実際のデータに加えて、headerと呼ばれるものの一部である他のフィールド(バイト単位でコード化された)が含まれます。通常、ヘッダーには、通信の状態に関連する情報(フラグ(ビット)など)、フレームカウンター、訂正およびエラー検出コードなどをエンコードするバイトが含まれます。フレーム内の送信データを取得し、フレームを使用してデータを送信する場合は、ビット単位の操作が必要になります。

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

具体的な例として、通信ハードウェアによって直接キャプチャされた生データを含むファイルを提供するものがあるとします。この場合、フレームを見つけるには、ファイル内の未加工バイトを読み取り、ビット単位でデータをスキャンして、ある種の同期ワードを見つける必要があります。同期ワードを特定したら、実際のフレームを取得し、必要に応じてSHIFTそれらを取得する必要があります(これがストーリーの始まりにすぎません) )送信されている実際のデータを取得します。

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

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

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

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

10
eguaio

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

x | 2

0001 //x
0010 //2

0011 //result = 3

x & 1

0001 //x
0001 //1

0001 //result = 1
6
Amarghosh

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

また、2 = 10なので、x | 2は111(7)ではなく110(6)であることに注意してください。加算のように動作します。

5
stonemetal

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

最近、x << 1とx >> 1を使用して2倍と2分の1を使用する人を見てきましたが、彼らが単に巧妙になろうとしているのか、通常の演算子よりも明確な利点があるのか​​はわかりません。

5
P. Stallworth

セット

セットは、数学演算を使用して結合できます。

  • ユニオン演算子|は、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
PUNEET JAIN

この例では、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]
3
dansalmo

別の一般的な使用例は、ファイルのアクセス許可の操作/テストです。 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()値を出力することは価値のある練習になるでしょう。

2
dbn

整数のビット表現は、ブール演算の配列を反復処理するよりもビットごとの操作がはるかに高速であるため、科学計算で真偽情報の配列を表すためによく使用されます。 (高レベル言語ではビット配列の概念を使用する場合があります。)

これのナイスでかなり単純な例は、Nimのゲームに対する一般的なソリューションです。 Python のコードをご覧ください Wikipediaページ 。ビットごとの排他的ORまたは^を多用します。

2
Jonah

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

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

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

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

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

pythonの場合:1&〜4 = 1(1から4を表すビットを削除します。この場合、1は「含む」ではありません4)。

1
Eyal Ch

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

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
woodfoot

次のビット演算子: 、、 ^ 、および の戻り値(入力に基づく)同じ方法 論理ゲート 信号に影響します。それらを使用して回路をエミュレートできます。

0
Erik Waters

ビットを反転するには(つまり、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'
0
user11034079

整数のビットを操作することは有用ですが、多くの場合、ネットワークプロトコルではビットまで指定できますが、長いバイトシーケンス(1つの整数に簡単に変換されない)の操作が必要になる場合があります。この場合、データのビットごとの操作を可能にする bitstring ライブラリを使用すると便利です。文字列「ABCDEFGHIJKLMNOPQ」を文字列として、または16進およびビットシフトとしてインポートすることができます(または他のビット演算を実行します):

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