ビット演算子で、データ構造内の個々の raw データビットを操作することができます。グラフィックプログラミングやデバイスドライバ開発のような、低レベルプログラミングでよく使用されています。また、カスタムプロトコル通信でのデータのエンコーディングやデコーディングのような、外部ソースからの raw データを扱うときにビット演算子は役立ちます。
以降での説明のように、Swift は C にあるビット演算子のすべてをサポートしています。
ビット NOT 演算子
ビット NOT 演算子 (~
) は、すべてのビット数値を反転します。
ビット NOT 演算子は前置演算子で、作用する値の直前に空白無しで現れます。
let initialBits: UInt8 = 0b00001111
let invertedBits = ~initialBits // 11110000 に等しい
UInt8
整数は、8 ビットで 0
から 255
までの値を保管できます。この例では UInt8
整数を、はじめの 4 ビットが 0
に、次の 4 ビットが 1
に設定された 2 進数値 00001111
で初期化しています。これは 10 進数値の 15
に相当します。
そして、ビット NOT 演算子は新しい定数 invertedBits
を生成するために使用されていて、initialBits
とすべてのビットが反転しています。0 は 1 に、そして 1 は 0 になります。invertedBits
の値は 11110000
で、これは符号無し 10 進数値の 240
に相当します。
ビット AND 演算子
ビット AND 演算子 (&
) は、2 つのビット数値を結合します。この演算子は、入力数値が共に 1
の場合にのみ、1
に設定された新しいビット数値を返します。
次の例では、firstSixBits
と lastSixBits
の値が共に、中央の 4 ビットが 1
になっています。ビット AND 演算子はこれらを数値 00111100
に結合し、これは符号無し 10 進数値 60
に相当します。
let firstSixBits: UInt8 = 0b11111100
let lastSixBits: UInt8 = 0b00111111
let middleFourBits = firstSixBits & lastSixBits // 00111100 に等しい
ビット OR 演算子
ビット OR 演算子 (|
) は、2 つのビット数値を比較します。この演算子は、入力数値のいずれかが 1
の場合に、1
に設定された新しいビット数値を返します。
次の例では、someBits
と moreBits
の値には 1
に設定された異なるビットがあります。ビット OR 演算子はこれらを数値 11111110
に結合し、これは符号無し 10 進数値 254
に相当します。
let someBits: UInt8 = 0b10110010
let moreBits: UInt8 = 0b01011110
let combinedbits = someBits | moreBits // 11111110 に等しい
ビット XOR 演算子
ビット XOR 演算子、あるいは排他的 OR 演算子 (^
) は、2 つのビット数値を比較します。この演算子は、入力ビットが異なる場合に 1
に設定され、入力ビットが同じ場合に 0
に設定された新しいビット数値を返します。
次の例では、firstBits
と otherBits
の値には、片方だけ 1
に設定されたビットがあります。ビット XOR 演算子は、そういったビットに出力値 1
を設定します。firstBits
と otherBits
が一致する他のビットはすべて、出力値 0
に設定されます。
let firstBits: UInt8 = 0b00010100
let otherBits: UInt8 = 0b00000101
let outputBits = firstBits ^ otherBits // 00010001 に等しい
ビット左シフト演算子とビット右シフト演算子
ビット左シフト演算子 (<<
) とビット右シフト演算子 (>>
) は、以降に定義されているルールに従って、すべてのビットを左または右に一定数だけ位置を移動します。
ビット左シフトとビット右シフトは、整数値を 2 の倍数分だけ乗算するか除算する効果があります。整数のビットを左に 1 桁シフトするとその値は 2 倍になり、右に 1 桁シフトするとその値は半分になります。
符号無し整数でのシフトの振る舞い
符号無し整数に対するビットシフトの振る舞いは、次のようになります。
- リクエストされた数だけ左または右に既存のビットを移動する。
- 整数の保管限度を越えて移動したビットを捨てる。
- もとのビットを左または右に移動した後、残された場所にゼロを入れる。
このアプローチは、論理シフトとして知られています。
次の図は、11111111 << 1
(11111111
を左に 1
桁シフト)と 11111111 >> 1
(11111111
を右に 1
桁シフト)の結果を示しています。青の数値はシフトされ、グレーの数値は捨てられ、オレンジのゼロは入れられた数値です。
次は、Swift でのビットシフトのコードです。
let shiftBits: UInt8 = 4 // 00000100(2 進法で)
shiftBits << 1 // 00001000
shiftBits << 2 // 00010000
shiftBits << 5 // 10000000
shiftBits << 6 // 00000000
shiftBits >> 2 // 00000001
他のデータタイプ内の値をエンコードおよびデコードするために、ビットシフトを使用することができます。
let pink: UInt32 = 0xCC6699
let redComponent = (pink & 0xFF0000) >> 16 // redComponent は 0xCC、または 204
let greenComponent = (pink & 0x00FF00) >> 8 // greenComponent は 0x66、または 102
let blueComponent = pink & 0x0000FF // blueComponent は 0x99、または 153
この例では、ピンク色のカスケーディングスタイルシートのカラー値を保管する UInt32
の定数 pink
を使用しています。CSS カラー値 #CC6699
は、Swift の 16 進数表現では 0xCC6699
と記述します。そして、このカラーをビット AND 演算子 (&
) とビット右シフト演算子 (>>
) で、赤 (CC
)、緑 (66
)、青 (99
) のコンポーネントに分解しています。
赤のコンポーネントは、数値 0xCC6699
と 0xFF0000
間のビット AND を実行して取得します。0xFF0000
のゼロは 0xCC6699
の 2 つ目と 3 つ目のバイトを効果的にマスクし、6699
を無視させて、結果として 0xCC0000
を残します。
そして、この数値を右に 16 桁 シフト (>> 16
) します。16 進数での文字の各ペアは 8 ビット使用するため、右に 16 桁の移動は、0xCC0000
を 0x0000CC
に変換します。これは、0xCC
と同じで、10 進数値で 204
になります。
同様に、緑のコンポーネントは、数値 0xCC6699
と 0x00FF00
間のビット AND を実行して取得し、出力値は 0x006600
になります。そして、この出力値を右に 8 桁シフトして 0x66
になり、10 進数値で 102
になります。
最後に、青のコンポーネントは、数値 0xCC6699
と 0x0000FF
間のビット AND を実行して取得し、出力値は 0x000099
になります。これを右にシフトする必要はなく、0x0099
は 0x99
に等しく、10 進数値で 153
になります。
符号付き整数でのシフトの振る舞い
シフトの振る舞いは、符号付き整数が 2 進法で表現されるため、符号無し整数よりも符号付き整数に対してのほうが複雑です。(以降の例は、簡単にするために 8 ビット符号付き整数をベースにしていますが、同じ原則があらゆるサイズの符号付き整数に適用されます。)
符号付き整数は、整数が正か負かをを示すために最初のビット(符号ビットとして知られる)を使用します。符号ビットの 0
は正を意味し、符号ビットの 1
は負を意味しています。
残りのビット(値ビットとして知られる)は実際の値を保管します。正の数値は、0
から上方へカウントする符号無し整数と同じように保管されます。次は、数値 4
の Int8
内のビットを示しています。
符号ビットは 0
(正を意味する)で、7 つの値ビットは 2 進法で記述された数値 4
です。
一方で、負の数値は異なります。n
を値ビットの数値として、2
の n
乗から絶対値を差し引いて保管されます。8 ビット数値には 7 つの値ビットがあり、2
の 7
乗の 128
になります。
次は、数値 -4
の Int8
内のビットを示しています。
今回は、符号ビットが 1
(負を意味する)で、7 つの値ビットは 124
(128 - 4
)の 2 進数値です。
負の数値のエンコーディングは、2 の補数表現として知られています。負の数値を表現する方法として普通でないようですが、いくつか利点があります。
はじめに、単純に(符号ビットを含め)8 ビットすべてに標準的な 2 進加算を実行し、その後 8 ビットに収まらない部分を捨てることで、-4
に -1
を加えることができます。
次に、2 の補数表現でも正数のように負数のビットを左右にシフトでき、すべてを左にシフトすることで 2 倍に、すべてを右にシフトすることで半分にします。これを達成するために、符号付き整数を右にシフトするときには追加のルールが使用されます。
- 符号付き整数を右にシフトするとき、符号無し整数と同じルールを適用しますが、左の空ビットにゼロでなく符号ビットで埋めます。
この振る舞いにより、右にシフトした後も同じ符号を持つ符号付き整数になり、算術シフトとして知られています。
正数と負数が保管される特別な方法により、右にシフトすることでゼロに近づいていきます。シフト時に符号ビットを維持することで、負数の値がゼロに近づきながらも負のままになります。
Portions of this page are translations based on work created and shared by Apple and used according to terms described in the Creative Commons Attribution 4.0 International License.