38

私は次のことを行う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に丸められます。

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

4

6 に答える 6

52

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

nPosVal = 00000000000000001111111111111111
nNegVal = 11111111111111110000000000000001

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

nPosVal = 00000000000000000111111111111111
nNegVal = 11111111111111111000000000000000

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

事実上、右シフトは負の無限大に向かって丸められます。

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

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

彼らはこれについて次のように述べいます。

C89 委員会は、K&R によって付与された実装の自由を確認し、符号付き右シフト操作を符号拡張する必要はありません。そのような要件は高速なコードを遅くする可能性があり、符号拡張シフトの有用性はわずかであるためです。(負の 2 の補数の整数を算術的に右に 1 桁シフトすることは、2 で割ることと同じではありません!)

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

于 2009-12-07T05:18:42.597 に答える
21

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

      65535: 00000000000000001111111111111111
     -65535: 11111111111111110000000000000001

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

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

10 進数に戻す:

 65535 >> 1 = 32767
-65535 >> 1 = -32768
于 2009-12-07T05:18:25.250 に答える
9

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

于 2009-12-07T05:15:36.473 に答える
3

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

0xFFFF = 0 1111 1111 1111 1111、右シフトして 0 0111 1111 1111 1111 = 0x7FFF

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

于 2009-12-07T05:14:47.223 に答える
3

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

A-2: シフトは除算と同じではありません。これはビット シフトであり、原始的な 2 進数演算です。いくつかのタイプの分割に使用できる場合があるのは便利ですが、常に同じであるとは限りません。

于 2009-12-07T05:18:16.093 に答える
2

C レベル以下のマシンには、完全に整数またはスカラーの CPU コアがあります。最近ではすべてのデスクトップ CPU に FPU が搭載されていますが、常にそうであるとは限らず、今日でも組み込みシステムは浮動小数点命令を使用せずに作られています。

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

そのため、CPU 命令は固定小数点演算を実装し、通常は純粋な整数演算として扱われます。プログラムがfloatまたはdoubleの項目を宣言する場合にのみ、分数が存在します。(まあ、分数で「固定小数点」に CPU ops を使用できますが、それは現在も常に非常にまれでした。)

何年も前に言語標準委員会によって要求されたものに関係なく、すべての合理的なマシンは、符号付き数値の右シフトで符号ビットを伝播します。符号なし値の右シフトは、左側のゼロにシフトします。右側にシフトアウトされたビットはフロアにドロップされます。

理解を深めるには、 「2 の補数演算」を調査する必要があります。

于 2009-12-07T05:23:06.180 に答える