48


14

符号付き整数の算術ビットシフト

Cで算術ビットシフト演算子が正確にどのように機能し、それが符号付き32ビット整数にどのように影響するかを理解しようとしています。

物事を簡単にするために、1バイト(8ビット)以内で作業するとします。

x = 1101.0101
MSB[ 1101.0101 ]LSB

Stack OverflowやいくつかのWebサイトの他の投稿を読んで、「<<」はMSBに向かってシフトし(私の場合は左に)、「空の」LSBビットを0で埋めます。

そして、「>>」はLSB(私の場合は右側)に向かってシフトし、「空の」ビットをMSビットで埋めます

したがって、 `x = x << 7`はLSBをMSBに移動し、すべてを0に設定します。

1000.0000

さて、最後の結果として「>> 7」としましょう。 これは `[0000.0010]`になりますか? 私は正しいですか?

シフト演算子に関する私の仮定については正しいですか?

私は自分のマシンでテストしました、**

int x = 1;   //000000000......01

x = x << 31; //100000000......00

x = x >> 31; //111111111......11 (Everything is filled with 1s !!!!!)

Why?

4 Answer


53


負の符号付き数値の右シフトには、実装定義の動作があります。

8ビットが符号付き8ビット値を表すことを意図している場合(8ビットの例に切り替える前に「符号付き32ビット整数」について話しているように)、負の数になります。 右にシフトすると、「空の」ビットが元のMSBで埋められる場合があります(つまり、 プラットフォームおよび/またはコンパイラに応じて、ゼロにシフトする可能性があります)。

(実装定義の動作とは、コンパイラーが賢明なことを行うことを意味しますが、プラットフォームに依存した方法で行われます。コンパイラーのドキュメントは何を伝えるかを想定しています。)

'' '' '

数値が負の値から始まる場合、またはシフト演算が符号ビット以上にシフトする場合、左シフトは未定義の動作をします(オーバーフローを引き起こす符号付きの値に対するほとんどの演算と同様)。

(未定義の動作とは、何でも起こる可能性があることを意味します。)

'' '' '

_unsigned_値に対する同じ操作は、どちらの場合でも明確に定義されています。「空の」ビットは0で埋められます。


34


ビット単位のシフト演算は負の値に対して定義されていません

「<<」

_ 6.5.7 / 4 […​] E1に符号付きのタイプと非負の値があり、E1×2 ^ E2 ^が結果のタイプで表現可能な場合、それが結果の値です。それ以外の場合、動作は未定義です。 _

および「>>」

_ 6.5.7 / 5 […​] E1が符号付きタイプと負の値を持っている場合、結果の値は実装定義です。 _

特定の実装の符号付き数値に対するこれらの操作の動作を研究するのは時間の無駄です。なぜなら、他の実装でも同じように動作する保証はないからです(実装とは、たとえば、コンピュータでコンパイラを特定のコンマ行パラメーター)。

まったく同じコンパイラの古いバージョンでも新しいバージョンでも動作しない可能性があります。 コンパイラーは、これらのビットをランダムまたは未定義として定義することもあります。 これは、ソース全体で使用した場合、まったく同じコードシーケンスがまったく異なる結果を生成したり、アセンブリの最適化やその他のレジスタの使用などに依存することさえあることを意味します。 関数にカプセル化されている場合、同じ引数を使用した2つの連続した呼び出しで、これらのビットで同じ結果が得られない場合があります。

負でない値のみを考慮する、左シフト1の効果(「式<< 1」)は、式を2倍すること(式* 2がオーバーフローしない場合)および右シフト1の効果と同じです。 ( expression >> 1)は2で除算するのと同じです。


5


他の人が言ったように、負の値のシフトは実装定義です。

ほとんどの実装では、符号付きビットを使用してシフトされたビットを埋めることにより、符号付き右シフトをfloor(x / 2 ^ N ^)として扱います。 この操作は非常に一般的であるため、実際には非常に便利です。 一方、右符号なし整数をシフトする場合、シフトされたビットはゼロになります。

マシン側から見ると、ほとんどの実装には2つのタイプの右シフト命令があります。

  1. 「算術」右シフト(多くの場合、ニーモニックASRまたはSRAを持つ) 私が説明したように動作します。

  2. 「論理」右シフト(ニーモニックLSRまたはSRLまたはSRを持つことが多い) 期待どおりに動作します。

ほとんどのコンパイラは、最初に符号付きの型に使用し、次に符号なしの型に使用します。 便宜上。


0


32ビットコンパイラー

x = x >> 31;

ここで、xは符号付き整数なので、32ビット目は符号ビットです。

*最終x値は100000 …​ 000 *です。 32ビット目は-ive値を示します。

ここで、xの値は1の賛辞に実装されます。

最終的なxは-32768です