web-dev-qa-db-ja.com

Cで負の数を右シフトする

私は次のようなCコードを持っています。

int nPosVal = +0xFFFF;   // + Added for ease of understanding
int nNegVal = -0xFFFF;   // - Added for valid reason

今私がしようとすると

printf ("%d %d", nPosVal >> 1, nNegVal >> 1);

私は得る

32767 -32768

これは予想されますか?

私は何かを考えることができます

65535 >> 1 = (int) 32767.5 = 32767
-65535 >> 1 = (int) -32767.5 = -32768

つまり、-32767.5は-32768に四捨五入されます。

この理解は正しいですか?

25
Alphaneo

あなたの実装はおそらく2の補数で算術ビットシフトを行っているようです。このシステムでは、すべてのビットを右にシフトし、最後のビットが何であったかのコピーで上位ビットを埋めます。したがって、例として、intをここでは32ビットとして扱います。

nPosVal = 00000000000000001111111111111111
nNegVal = 11111111111111110000000000000001

シフト後、次のものが得られます。

nPosVal = 00000000000000000111111111111111
nNegVal = 11111111111111111000000000000000

これを10進数に戻すと、それぞれ32767と-32768になります。

事実上、右シフトは負の無限大に丸めます。

編集:最新の ドラフト標準 のセクション6.5.7によると、負の数に対するこの動作は実装に依存します。

E1 >> E2の結果は、E1の右シフトされたE2ビット位置です。 E1が符号なしの型である場合、またはE1が符号付きの型で負でない値である場合、結果の値はE1/2の商の整数部分ですE2。 E1に符号付きの型と負の値がある場合、結果の値は実装定義です。

彼らの述べた 合理的 これについて:

C89委員会は、符号付き右シフト演算に符号拡張を要求しないことで、K&Rによって許可された実装の自由を認めました。 (負の2の補数整数を算術的に右に1桁シフトすると、 ない 2で割るのと同じです!)

したがって、理論的には実装に依存します。実際には、実装を見たことがない ない 左のオペランドが符号付きの場合、算術右シフトを実行します。

39
Boojum

いいえ、整数を使用する場合、0.5のような小数は得られません。 2つの数値のバイナリ表現を見ると、結果は簡単に説明できます。

      65535: 00000000000000001111111111111111
     -65535: 11111111111111110000000000000001

右に1ビットシフトし、左に拡張します(これは実装に依存することに注意してください。Trentに感謝します)。

 65535 >> 1: 00000000000000000111111111111111
-65535 >> 1: 11111111111111111000000000000000

10進数に戻す:

 65535 >> 1 = 32767
-65535 >> 1 = -32768
18
Mark Byers

C仕様では、符号ビットがシフトされるかどうかは指定されていません。実装に依存します。

7
Trent

右シフトすると、最下位ビットが破棄されます。

0xFFFF = 0 1111 1111 11111111。右シフトすると0 0111 1111 1111 1111 = 0x7FFF

-0xFFFF = 1 0000 0000 0000 0001(2の補数)、右シフトして1 1000 0000 0000 0000 = -0x8000

3
Anon.

A-1:はい。 0xffff >> 1は0x7fffまたは32767です。-0xffffが何をするのかわかりません。それは独特です。

A-2:シフトは分割と同じことではありません。これは、ビットシフト、つまり基本的なバイナリ演算です。一部の種類の除算に使用できることは便利ですが、常に同じとは限りません。

3
wallyk

Cレベルの下に、マシンには完全に整数またはスカラーのCPUコアがあります。最近では、すべてのデスクトップCPUにFPUが搭載されていますが、これは常に当てはまるわけではなく、今日でも組み込みシステムは浮動小数点命令なしで作られています。

今日のプログラミングパラダイムとCPUの設計と言語は、FPUさえ存在しなかった時代にさかのぼります。

したがって、CPU命令は固定小数点演算を実装し、一般に純粋にintegerとして扱われますops。プログラムがfloatまたはdoubleの項目を宣言する場合のみ、小数部が存在します。 (まあ、CPU演算を小数の「固定小数点」に使用できますが、これは今や常に非常にまれでした。)

何年も前に言語標準委員会が要求したものに関係なく、すべての妥当なマシンは、符号付き数値の右シフトで符号ビットを伝搬します。符号なしの値の右シフトは、左側でゼロにシフトします。右側にシフトアウトされたビットは、床に落とされます。

あなたの理解を深めるためには、調査する必要があります"2の補数の算術"

2
DigitalRoss