見出し画像

Swiftでプログラミング-Advanced Operators 1

基本演算子で説明されている演算子に加えて、Swiftは、より複雑な値操作を実行するいくつかの高度な演算子を提供します。これらには、CおよびObjective-Cでおなじみのビット単位およびビットシフト演算子がすべて含まれています。

Cの算術演算子とは異なり、Swiftの算術演算子はデフォルトではオーバーフローしません。オーバーフロー動作はトラップされ、エラーとして報告されます。オーバーフロー動作をオプトインするには、オーバーフロー加算演算子(&+)など、デフォルトでオーバーフローするSwiftの2番目の算術演算子のセットを使用します。これらのオーバーフロー演算子はすべて、アンパサンド(&)で始まります。

独自の構造、クラス、および列挙を定義する場合、これらのカスタム型の標準Swift演算子の独自の実装を提供すると便利な場合があります。 Swiftを使用すると、これらの演算子のカスタマイズされた実装を簡単に提供し、作成するタイプごとにそれらの動作を正確に決定できます。

事前定義された演算子に限定されません。 Swiftを使用すると、カスタムの優先順位と結合性の値を使用して、独自のカスタムのインフィックス、プレフィックス、ポストフィックス、および代入演算子を自由に定義できます。これらの演算子は、定義済みの演算子と同じようにコードで使用および採用できます。また、既存の型を拡張して、定義したカスタム演算子をサポートすることもできます。

Bitwise Operators

ビット演算子を使用すると、データ構造内の個々の生データビットを操作できます。 グラフィックプログラミングやデバイスドライバの作成など、低レベルのプログラミングでよく使用されます。 ビット単位の演算子は、カスタムプロトコルを介した通信用のデータのエンコードやデコードなど、外部ソースからの生データを操作する場合にも役立ちます。

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

Bitwise NOT Operator

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

ビット単位のNOT演算子はプレフィックス演算子であり、操作する値の直前に空白なしで表示されます。

   let initialBits: UInt8 = 0b00001111
   let invertedBits = ~initialBits  // equals 11110000

UInt8整数は8ビットで、0〜255の任意の値を格納できます。この例では、UInt8整数をバイナリ値00001111で初期化します。このバイナリ値では、最初の4ビットが0に設定され、次の4ビットが1に設定されます。これは次のようになります。 15の10進値。

次に、ビット単位のNOT演算子を使用して、invertedBitsと呼ばれる新しい定数を作成します。これはinitialBitsと同じですが、すべてのビットが反転されています。 ゼロは1になり、1はゼロになります。 ConvertedBitsの値は11110000で、これは符号なし10進値240に相当します。

Bitwise AND Operator

ビットごとのAND演算子(&)は、2つの数値のビットを結合します。 両方の入力番号のビットが1に等しい場合にのみ、ビットが1に設定された新しい番号を返します。

以下の例では、firstSixBitsとlastSixBitsの両方の値に1に等しい4つの中間ビットがあります。ビット単位のAND演算子は、それらを組み合わせて、符号なし10進値60に等しい数値00111100を作成します。

   let firstSixBits: UInt8 = 0b11111100
   let lastSixBits: UInt8  = 0b00111111
   let middleFourBits = firstSixBits & lastSixBits  // equals 00111100

Bitwise OR Operator

ビットごとのOR演算子(|)は、2つの数値のビットを比較します。 演算子は、いずれかの入力番号のビットが1に等しい場合、ビットが1に設定されている新しい番号を返します。

以下の例では、someBitsとmoreBitsの値のビットが1に設定されています。ビット単位のOR演算子を組み合わせて、符号なし10進数の254に等しい数値11111110を作成します。

   let someBits: UInt8 = 0b10110010
   let moreBits: UInt8 = 0b01011110
   let combinedbits = someBits | moreBits  // equals 11111110

Bitwise XOR Operator

ビット単位のXOR演算子、または「排他的論理和演算子」(^)は、2つの数値のビットを比較します。 演算子は、入力ビットが異なる場合はビットが1に設定され、入力ビットが同じ場合は0に設定された新しい数値を返します。

以下の例では、firstBitsとotherBitsの値はそれぞれ、他のビットが設定されていない場所で1に設定されたビットを持っています。 ビット単位のXOR演算子は、これらのビットの両方を出力値で1に設定します。 firstBitsとotherBitsの他のすべてのビットは一致し、出力値で0に設定されます。

   let firstBits: UInt8 = 0b00010100
   let otherBits: UInt8 = 0b00000101
   let outputBits = firstBits ^ otherBits  // equals 00010001

Bitwise Left and Right Shift Operators

ビット単位の左シフト演算子(<<)およびビット単位の右シフト演算子(>>)は、以下に定義する規則に従って、数値のすべてのビットを特定の桁数だけ左または右に移動します。

ビット単位の左シフトと右シフトには、整数を2倍に乗算または除算する効果があります。 整数のビットを1桁左にシフトすると、その値が2倍になりますが、右に1桁シフトすると、値が半分になります。

Shifting Behavior for Unsigned Integers

符号なし整数のビットシフト動作は次のとおりです。

既存のビットは、要求された桁数だけ左または右に移動されます。
整数のストレージの境界を超えて移動されたビットはすべて破棄されます。
元のビットが左または右に移動された後、残されたスペースにゼロが挿入されます。

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

次の図は、11111111 << 1(11111111が1桁左にシフト)および11111111 >> 1(11111111が右に1桁シフト)の結果を示しています。 青い数字がシフトされ、灰色の数字が破棄され、オレンジ色のゼロが挿入されます。

Swiftコードでのビットシフトの外観は次のとおりです。

   let shiftBits: UInt8 = 4   // 00000100 in binary
   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 is 0xCC, or 204
   let greenComponent = (pink & 0x00FF00) >> 8   // greenComponent is 0x66, or 102
   let blueComponent = pink & 0x0000FF           // blueComponent is 0x99, or 153

この例では、ピンクと呼ばれるUInt32定数を使用して、ピンク色のカスケードスタイルシートの色値を格納します。 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に変換されます。これは、10進値が204の0xCCと同じです。

同様に、緑色のコンポーネントは、数値0xCC6699と0x00FF00の間でビット単位のANDを実行することによって取得されます。これにより、出力値は0x006600になります。次に、この出力値は8桁右にシフトされ、10進値が102の0x66の値が得られます。

最後に、青色のコンポーネントは、数値0xCC6699と0x0000FFの間でビット単位のANDを実行することによって取得されます。これにより、出力値は0x000099になります。 0x000099はすでに0x99と等しく、10進値は153であるため、この値は右にシフトせずに使用されます。

Shifting Behavior for Signed Integers

符号付き整数が2進数で表される方法のため、シフト動作は符号なし整数よりも符号付き整数の方が複雑です。 (以下の例は、簡単にするために8ビットの符号付き整数に基づいていますが、同じ原則が任意のサイズの符号付き整数に適用されます。)

符号付き整数は、最初のビット(符号ビットと呼ばれます)を使用して、整数が正か負かを示します。 符号ビット0は正を意味し、符号ビット1は負を意味します。

残りのビット(値ビットと呼ばれる)は、実際の値を格納します。 正の数は、符号なし整数の場合とまったく同じ方法で格納され、0から上向きにカウントされます。Int8内のビットが数値4を探す方法は次のとおりです。

符号ビットは0(「正」を意味します)であり、7つの値ビットは2進表記で書かれた4の数字です。

ただし、負の数の格納方法は異なります。 これらは、絶対値を2からnの累乗で減算することによって格納されます。ここで、nは値のビット数です。 8ビットの数値には7つの値ビットがあるため、これは2の7乗、つまり128を意味します。

Int8内のビットが数字-4を探す方法は次のとおりです。

今回は、符号ビットは1(「負」を意味する)であり、7つの値ビットのバイナリ値は124(128-4)です。

この負の数のエンコードは、2の補数表現として知られています。 負の数を表すのは珍しい方法のように思われるかもしれませんが、いくつかの利点があります。

まず、8ビットすべて(符号ビットを含む)の標準的な2進加算を実行し、完了したら8ビットに収まらないものをすべて破棄するだけで、-1から-4を加算できます。

次に、2の補数表現を使用すると、負の数のビットを正の数のように左右にシフトしても、左にシフトするたびに2倍にするか、右にシフトするたびに半分にすることができます。 。 これを実現するために、符号付き整数を右にシフトするときに追加のルールが使用されます。符号付き整数を右にシフトするときは、符号なし整数と同じルールを適用しますが、左側の空のビットを符号ビットで埋めます。 ゼロよりも。

このアクションにより、符号付き整数は右にシフトされた後も同じ符号を持つようになります。これは算術シフトと呼ばれます。

正の数と負の数が格納される特別な方法のため、どちらかを右にシフトすると、それらはゼロに近づきます。 このシフト中に符号ビットを同じに保つことは、負の整数の値がゼロに近づくにつれて負のままになることを意味します。

Overflow Operators

その値を保持できない整数定数または変数に数値を挿入しようとすると、デフォルトでは、Swiftは無効な値の作成を許可するのではなく、エラーを報告します。 この動作により、数値が大きすぎたり小さすぎたりする場合に、安全性がさらに高まります。

たとえば、Int16整数型は、-32768〜32767の任意の符号付き整数を保持できます。Int16定数または変数をこの範囲外の数値に設定しようとすると、エラーが発生します。

   var potentialOverflow = Int16.max
   // potentialOverflow equals 32767, which is the maximum value an Int16 can hold
   potentialOverflow += 1
   // this causes an error

値が大きすぎたり小さすぎたりするときにエラー処理を提供すると、境界値条件をコーディングする際の柔軟性が大幅に向上します。

ただし、オーバーフロー条件で使用可能なビット数を切り捨てる必要がある場合は、エラーをトリガーするのではなく、この動作をオプトインできます。 Swiftは、整数計算のオーバーフロー動作をオプトインする3つの算術オーバーフロー演算子を提供します。 これらの演算子はすべてアンパサンド(&)で始まります。

オーバーフローの追加(&+)
オーバーフロー減算(&-)
オーバーフロー乗算(&*)

Value Overflow

数値は正と負の両方の方向にオーバーフローする可能性があります。

オーバーフロー加算演算子(&+)を使用して、符号なし整数が正の方向にオーバーフローできる場合の例を次に示します。

   var unsignedOverflow = UInt8.max
   // unsignedOverflow equals 255, which is the maximum value a UInt8 can hold
   unsignedOverflow = unsignedOverflow &+ 1
   // unsignedOverflow is now equal to 0

変数unsignedOverflowは、UInt8が保持できる最大値(255、またはバイナリでは11111111)で初期化されます。 次に、オーバーフロー加算演算子(&+)を使用して1ずつインクリメントされます。 これにより、次の図に示すように、UInt8が保持できるサイズを超えてバイナリ表現がプッシュされ、境界を超えてオーバーフローします。 オーバーフローの追加後にUInt8の範囲内にとどまる値は、00000000、つまりゼロです。

符号なし整数が負の方向にオーバーフローすることが許可されている場合も、同様のことが起こります。 オーバーフロー減算演算子(&-)を使用した例を次に示します。

   var unsignedOverflow = UInt8.min
   // unsignedOverflow equals 0, which is the minimum value a UInt8 can hold
   unsignedOverflow = unsignedOverflow &- 1
   // unsignedOverflow is now equal to 255

UInt8が保持できる最小値はゼロ、またはバイナリでは00000000です。 オーバーフロー減算演算子(&-)を使用して00000000から1を減算すると、数値はオーバーフローし、11111111、つまり10進数で255にラップアラウンドします。

符号付き整数でもオーバーフローが発生します。 符号付き整数のすべての加算と減算はビット単位で実行され、ビット単位の左シフト演算子と右シフト演算子で説明されているように、加算または減算される数値の一部として符号ビットが含まれます。

var signedOverflow = Int8.min
// signedOverflow equals -128, which is the minimum value an Int8 can hold
signedOverflow = signedOverflow &- 1
// signedOverflow is now equal to 12

Int8が保持できる最小値は、-128、つまりバイナリでは10000000です。 オーバーフロー演算子を使用してこの2進数から1を引くと、01111111の2進数値が得られます。これにより、符号ビットが切り替わり、Int8が保持できる最大の正の値である正の127が得られます。

符号付き整数と符号なし整数の両方で、正の方向のオーバーフローは有効な最大値から最小値に戻り、負の方向のオーバーフローは最小値から最大値に折り返されます。




この記事が気に入ったらサポートをしてみませんか?