3

C# のシフト演算子を扱っているときに、左シフト演算子の予期しない動作に遭遇しました。
次に、この単純な関数を試しました:

for (int i = 5; i >= -10; i--) {
    int s = 0x10 << i;
    Debug.WriteLine(i.ToString().PadLeft(3) + "   " + s.ToString("x8"));
}

この結果:

  5   00000200
  4   00000100
  3   00000080
  2   00000040
  1   00000020
  0   00000010
 -1   00000000     -> 00000008 expected
 -2   00000000     -> 00000004 expected
 -3   00000000     -> 00000002 expected
 -4   00000000     -> 00000001 expected
 -5   80000000
 -6   40000000
 -7   20000000
 -8   10000000
 -9   08000000
-10   04000000

今日まで、<<演算子は第 2 オペランドの負の値を処理できると思っていました。
MSDNは、2 番目のオペランドに負の値を使用した場合の動作について何も伝えていません。しかし、MSDN によると、演算子は下位 5 ビット (0 ~ 31) のみを使用し、負の値に適合するはずです。

long私もvalues:で試しましlong s = 0x10L << i;たが、同じ結果になりました。

それで、ここで何が起こりますか?

編集
回答で述べたように、負の値の表現はこれの理由ではありません。
すべてのケースで同じ間違った結果が得られました。

0x10<<-3                    = 0x00000000    (wrong!)
0x10<<(int)(0xfffffffd)     = 0x00000000    (wrong!)
0x10<<(0x0000001d           = 0x00000000    (wrong!)
                   expected = 0x00000002

編集 #2
これら 2 つのうちの 1 つが true である必要があります。

1) シフト演算子は実際のシフト演算子であるため、結果は次のようになります:
1a) 0x10 << -3 = 00000002
1b)0x10 << -6 = 00000000

2) シフト演算子は回転演算子なので、結果は次のようになります:
2a) 0x10 << -3 = 00000002 (1a) と同じ
2b)0x10 << -6 = 40000000

しかし、表示された結果は 1) にも 2) にも当てはまりません!!!

4

3 に答える 3

3

左シフト演算子は、負の 2 番目のオペランドを右シフトとして認識しません。値の下位 5 ビットのみを使用し、それを使用して左シフトを行います。

-1値( 0xFFFFFFFF)の下位 5 ビットは31( )0x0000001Fになるため、最初のオペランド0x10は左に 31 ステップシフトされ、結果の最上位ビットに最下位ビットだけが残ります。

つまり、は になる0x10 << -1のと同じですが、結果は 32 ビットなので に切り捨てられます。0x10 << 310x8000000000x00000000

long 値を使用している場合、2 番目のオペランドの最下位 6 ビットが使用されます。値-1は 63 になり、ビットは引き続き long の範囲外にシフトアウトされます。

于 2013-08-08T13:00:41.537 に答える
3

負の数は 2 の補数なので-1 == 0xFFFFFFFF、 、0xFFFFFFFF & 31 == 31-2 == 0xFFFFFFFE、 など0xFFFFFFFE & 31 == 30です。

-10 == 0xFFFFFFF6, and 0xFFFFFFF6 & 31 == 22, in fact:

(0x10 << 22) == 04000000

表示するコード:

const int num = 0x10;
int maxShift = 31;

for (int i = 5; i >= -10; i--)
{
    int numShifted = num << i;
    uint ui = (uint)i;
    int uiWithMaxShift = (int)(ui & maxShift);
    int numShifted2 = num << uiWithMaxShift;

    Console.WriteLine("{0,3}: {1,8:x} {2,2} {3,8:x} {4,8:x} {5}",
        i,
        ui,
        uiWithMaxShift,
        numShifted,
        numShifted2,
        numShifted == numShifted2);
}

それは同じですlongが、代わりに. 、および& 31& 63-1 == 63-2 == 62-10 == 54

いくつかのコード例:

const long num = 0x10;
int maxShift = 63;

for (int i = 5; i >= -10; i--)
{
    long numShifted = num << i;
    uint ui = (uint)i;
    int uiWithMaxShift = (int)(ui & maxShift);
    long numShifted2 = num << uiWithMaxShift;

    Console.WriteLine("{0,3}: {1,8:x} {2,2} {3,16:x} {4,16:x} {5}", 
        i, 
        ui, 
        uiWithMaxShift, 
        numShifted, 
        numShifted2, 
        numShifted == numShifted2);
}

ただ明確にします:

(int x) << y == (int x) << (int)(((uint)y) & 31)
(long x) << y == (long x) << (int)(((uint)y) & 63)

そしてそうではない

(int x) << y == (int x) << (Math.Abs(y) & 63)
(long x) << y == (long x) << (Math.Abs(y) & 63)

そして、あなたが「あるべきだ」「そうであれば美しいだろう」「そうでなければならない」と思うものは ecc とは無関係です . 1 と 0 は「近い」(バイナリ表現の「距離」は異なるビット数で 1 です) のに対し、0 と -1 は「遠い」です (バイナリ表現の「距離」はビット数で 32 または 64 です)。異なるビット)

あなたはこれを取得する必要があると思います:

-1   00000000     -> 00000008 expected
-2   00000000     -> 00000004 expected
-3   00000000     -> 00000002 expected
-4   00000000     -> 00000001 expected

しかし実際には、あなたが見ていないのは、あなたがこれを得ているということです:

-1   (00000008) 00000000
-2   (00000004) 00000000
-3   (00000002) 00000000
-4   (00000001) 00000000
-5   (00000000) 80000000 <-- To show that "symmetry" and "order" still exist
-6   (00000000) 40000000 <-- To show that "symmetry" and "order" still exist

の部分(...)は、の「左」にあり、int存在しない部分です。

于 2013-08-08T12:57:30.340 に答える
3

これは、負の数の表現に関係しています。-1すべて 1 に対応するため、最下位 5 ビットの合計は 31 になり0x10、31 ビット左シフトするとすべてゼロになります (ドキュメントに従って、既に設定されている上位ビットは破棄されます)。

ますます大きくなる負の数は、30、29、などのビットのシフトに対応します。すでに設定されているビットは0x10ゼロベースの位置 4 にあるため、それを破棄しないためには、シフトは最大で 31 - 4 = 27 ビットである必要があり、これはi == -5.

たとえば、次のようにすると、何が起こっているかを簡単に確認できますConsole.WriteLine((-1).ToString("x8"))

ffffffff

更新:第 1 オペランドが のlong場合、第 2 オペランドから 6 つの最下位ビットがカウントされるため、同様の動作が見られます:0x10L << -1左に 63 ビットをシフトするなど。

于 2013-08-08T12:58:28.290 に答える