ビット演算子で、データ構造内の個々の raw データビットを操作することができます。グラフィックプログラミングやデバイスドライバ開発のような、低レベルプログラミングでよく使用されています。また、カスタムプロトコル通信でのデータのエンコーディングやデコーディングのような、外部ソースからの raw データを扱うときにビット演算子は役立ちます。

以降での説明のように、Swift は C にあるビット演算子のすべてをサポートしています。

ビット NOT 演算子

ビット NOT 演算子 (~) は、すべてのビット数値を反転します。

image: bitwiseNOT_2x

ビット 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 に設定された新しいビット数値を返します。

image: bitwiseAND_2x

次の例では、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 に設定された新しいビット数値を返します。

image: bitwiseOR_2x

次の例では、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 に設定された新しいビット数値を返します。

image: bitwiseXOR_2x

次の例では、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 桁シフトするとその値は半分になります。

符号無し整数でのシフトの振る舞い

符号無し整数に対するビットシフトの振る舞いは、次のようになります。

  1. リクエストされた数だけ左または右に既存のビットを移動する。
  2. 整数の保管限度を越えて移動したビットを捨てる。
  3. もとのビットを左または右に移動した後、残された場所にゼロを入れる。

このアプローチは、論理シフトとして知られています。

次の図は、11111111 << 111111111 を左に 1 桁シフト)と 11111111 >> 111111111 を右に 1 桁シフト)の結果を示しています。青の数値はシフトされ、グレーの数値は捨てられ、オレンジのゼロは入れられた数値です。

image: bitshiftUnsigned_2x

次は、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 内のビットを示しています。

image: bitshiftSignedFour_2x

符号ビットは 0(正を意味する)で、7 つの値ビットは 2 進法で記述された数値 4 です。

一方で、負の数値は異なります。n を値ビットの数値として、2 の n 乗から絶対値を差し引いて保管されます。8 ビット数値には 7 つの値ビットがあり、2 の 7 乗の 128 になります。

次は、数値 -4 の Int8 内のビットを示しています。

image: bitshiftSignedMinusFour_2x

今回は、符号ビットが 1(負を意味する)で、7 つの値ビットは 124128 - 4)の 2 進数値です。

image: bitshiftSignedMinusFourValue_2x

負の数値のエンコーディングは、2 の補数表現として知られています。負の数値を表現する方法として普通でないようですが、いくつか利点があります。

はじめに、単純に(符号ビットを含め)8 ビットすべてに標準的な 2 進加算を実行し、その後 8 ビットに収まらない部分を捨てることで、-4 に -1 を加えることができます。

image: bitshiftSignedAddition_2x

次に、2 の補数表現でも正数のように負数のビットを左右にシフトでき、すべてを左にシフトすることで 2 倍に、すべてを右にシフトすることで半分にします。これを達成するために、符号付き整数を右にシフトするときには追加のルールが使用されます。

  • 符号付き整数を右にシフトするとき、符号無し整数と同じルールを適用しますが、左の空ビットにゼロでなく符号ビットで埋めます。

image: bitshiftSigned_2x

この振る舞いにより、右にシフトした後も同じ符号を持つ符号付き整数になり、算術シフトとして知られています。

正数と負数が保管される特別な方法により、右にシフトすることでゼロに近づいていきます。シフト時に符号ビットを維持することで、負数の値がゼロに近づきながらも負のままになります。


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.