知識ベース

算術シフト

コンピュータープログラミングでは、 算術シフトはシフト演算子であり、 符号付きシフトと呼ばれることもあります(ただし、符号付きオペランドに限定されません)。 2つの基本タイプは、 算術左シフト算術右シフトです。 2進数の場合、オペランドのすべてのビットをシフトするビット演算です。オペランドのすべてのビットは、指定された数のビット位置に移動され、空のビット位置が埋められます。論理シフトのようにすべて0で埋められる代わりに、右にシフトするとき、左端のビット(通常は符号付き整数表現の符号ビット)は、すべての空いている位置を埋めるために複製されます(これは一種の符号拡張です)。

一部の著者は、算術シフトと論理シフトに対してそれぞれスティッキー右シフトゼロフィル右シフトという用語を好んでいます。

算術シフトは、符号付き整数の2の累乗による乗算または除算を実行する効率的な方法として役立ちます。符号付きまたは符号なしの2進数でnビット左にシフトすると、2 nで乗算されます。 2の補数の符号付き 2進数でnビット右にシフトすると、2 nで除算されますが、常に切り捨てられます(負の無限大に向かって)。これは、符号付き整数除算で丸めが通常行われる方法(0に丸める)とは異なります。この矛盾により、複数のコンパイラでバグが発生しました。

たとえば、x86命令セットでは、SAR命令(算術右シフト)は符号付き数値を2のべき乗で除算し、負の無限大に丸めます。ただし、IDIV命令(符号付き除算)は、符号付き数値をゼロに丸めます。したがって、SAR命令を2の累乗命令でIDIVに置き換えることはできません。

正式な定義

連邦標準1037Cからの算術シフトの正式な定義は、次のとおりです。

固定基数記数法システムおよび固定小数点表現システムで数値の表現に適用されるシフト。数値の固定小数点部分を表す文字のみが移動します。算術シフトは、通常、丸めの効果を除いて、基数の正または負の整数の累乗を数値に乗算することと同等です。特に浮動小数点表現の場合、論理シフトを算術シフトと比較します。

FS 1073Cの定義で重要な言葉は「通常」です。

算術および論理左シフトと乗算の等価性

算術シフトは、基数の(正の整数)乗による乗算と同等です(たとえば、2進数の2の累乗による乗算)。算術左シフトは、1つの例外を除いて、論理的な左シフトと実質的に同じです。算術シフトは算術オーバーフローを引き起こす可能性がありますが、論理シフトはそうではありません。この例外は、このようなオーバーフローのトリガー信号が必要な場合にのみ重要です。

算術右シフトと除算の非等価性

ただし、算術シフトは、特に負の整数の丸めを処理する際の不注意な点の主要なトラップです。たとえば、通常の負の整数の2の補数表現では、-1はすべて1として表されます。 8ビットの符号付き整数の場合、これは1111 1111です。1(または2、3、…、7)の算術右シフトにより、再び1111 1111が得られますが、これはまだ-1です。これは切り捨て(負の無限大に向かう)に対応しますが、通常の除算の規則ではありません。

算術右シフトは基数の(正の整数)乗による除算(たとえば、2進数の2の累乗による除算)と同等であるため、基数の累乗による除算は算術右シフトとして実装することにより最適化されます。 (シフターは除算器よりもはるかに単純です。ほとんどのプロセッサーでは、シフト命令は除算命令よりも高速に実行されます。)DEC、IBM、Data Generalなどの企業や機関からの多数の1960年代および1970年代のプログラミングハンドブック、マニュアル、およびその他の仕様、およびANSIはそのような誤った記述を行います。

論理的な右シフトは、正または符号なしの数値についてのみ、基数の累乗(通常は2)による除算と同等です。算術右シフトは、正の符号付き数値の論理右シフトと同等です。 N-1の補数(通常は2の補数)の負の数の算術右シフトは、基数の累乗(通常2)にほぼ等しく、奇数に下向きの丸めが適用されます(通常は0に向かってではありません)。

負の数値の算術右シフトは、一部の歴史的なコンピューターで使用されていた符号付き数値の補数表現での0方向への丸めを使用した除算に相当しますが、これはもはや一般的に使用されていません

プログラミング言語での問題の処理

プログラミング言語Cの(1999)ISO標準は、2の累乗による除算の観点から右シフト演算子を定義します。上記の非等価性のため、標準はその定義から符号付き数値の右シフトを明示的に除外します負の値。そのような状況での右シフト演算子の動作を指定するのではなく、負の値を右にシフトする動作を定義するために、個々のCコンパイラが必要です。

用途

一貫した切り捨てが必要なアプリケーションでは、符号付き値の算術右シフトが便利です。例は、2のべき乗によるラスタ座標のダウンスケーリングであり、均等な間隔が維持されます。たとえば、1だけ右シフトすると、0、1、2、3、4、5、…が0、0、1、1、2、2、…、および-1、-2、-3、-4、…に送信されます。 -1、-1、-2、-2、…、等間隔を-2、-2、-1、-1、0、0、1、1、2、2、…と維持しますゼロへの丸めは、-1、0、および1をすべて0に送信し(2ではなく3ポイント)、-2、-1、-1、0、0、0、1、1、2、2、…を代わりに生成します。 0で不規則です。

ノート

  1. ^ CおよびC ++の>>演算子は、必ずしも算術シフトではありません。通常、左辺に符号付き整数型を使用する場合、算術シフトのみです。代わりに符号なし整数型で使用される場合、 論理シフトになります。
  2. ^ Verilogの算術右シフト演算子は、第1オペランドが符号付きの場合にのみ実際に算術シフトを実行します。最初のオペランドが符号なしの場合、演算子は実際に論理右シフトを実行します。
  3. ^ OpenVMSマクロ言語では、算術シフトが左か右かは、第2オペランドが正か負かによって決まります。これは異常です。ほとんどのプログラミング言語では、2つの方向に異なる演算子があり、演算子が方向を指定し、第2オペランドは暗黙的に正です。 (Verilogなどの一部の言語では、負の値を符号なしの正の値に変換する必要があります。CやC ++などの一部の言語では、負の値を使用すると動作が定義されません。)
  4. ^ Schemeでは、第2オペランドに応じて、算術シフトは左シフトと右シフトの両方が可能で、OpenVMSマクロ言語に非常に似ていますが、R6RS Schemeは-rightと-leftの両方のバリアントを追加します。
  5. ^ HaskellのData.BitsモジュールのBitsクラスは、符号付き引数を取るshiftと符号なし引数を取るshiftL / shiftRの両方を定義します。これらは同型です。新しい定義の場合、プログラマは2つの形式のうち1つだけを提供する必要があり、他の形式は提供された形式に関して自動的に定義されます。
  6. ^ VHDL算術左シフト演算子は異常です。結果のLSBをゼロで埋める代わりに、元のLSBを新しいLSBにコピーします。これは算術右シフトの正確な鏡像ですが、演算子の従来の定義ではなく、2の累乗による乗算と同等ではありません。VHDL2008標準では、この奇妙な動作は変更されませんでした(後方互換性のため) )数値の強制解釈を行わない引数タイプ(BIT_VECTORなど)に対して、 符号なしおよび符号 付き引数タイプの 'SLA'は期待どおりに動作します(つまり、右端の位置はゼロで埋められます)。 VHDLの左シフト論理(SLL)機能は、前述の「標準」算術シフトを実装します。
  7. ^ C標準は、C言語を1の補数または2の補数アーキテクチャに制限しないことを目的としていました。このように、1の補数と2の補数表現の動作が異なる場合、標準では、個々のCコンパイラがターゲットアーキテクチャの動作を文書化することを要求しています。たとえば、GNU Compiler Collection(GCC)のドキュメントには、符号拡張を使用する場合の動作が記載されています。