15

難しい方法を学び、x86マシンで32ビット以上に左シフトしてみましlong longた。私は漠然と、32ビットのマシンシフト演算子よりもどこかで読んだことを覚えています。シフト演算子は最初の32ビットでのみ機能し、ソースを再収集することはできません。x86マシンで32ビットを超えるuint64_t整数をシフトすることが未定義動作であるかどうかを知りたいのですが。uint64_t0

4

5 に答える 5

24

標準によると(n1570では6.5.7):

3整数拡張は、各オペランドで実行されます。結果のタイプは、プロモートされた左オペランドのタイプです。右のオペランドの値が負であるか、プロモートされた左のオペランドの幅以上の場合、動作は定義されていません。

4 E1 << E2の結果は、E1が左にシフトされたE2ビット位置です。空になったビットはゼロで埋められます。E1に符号なしタイプがある場合、結果の値はE1×2 E2であり、結果タイプで表現可能な最大値より1を法として減少します。E1に符号付きタイプと非負の値があり、E1×2 E2が結果タイプで表現可能である場合、それが結果の値です。それ以外の場合、動作は定義されていません。

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

uint64_t64ビット未満の距離のシフトは、標準で完全に定義されています。

long long少なくとも64ビットである必要があるため、結果がオーバーフローしない場合、64ビット未満のシフト値long longは非負の値の標準で定義されています。

ただし、たとえばuint64_t s = 1 << 32@drhirschが推測するように、32ビットに収まるリテラルを作成する場合、実際には64ビット値ではなく32ビット値をシフトすることに注意してください。それは未定義の振る舞いです。

最も一般的な結果はshift_distance % 32、ハードウェアの動作に応じて、または0のシフトです(コンパイラのコンパイル時の評価が、鼻の悪魔ではなく、ハードウェアのセマンティクスをエミュレートすると仮定します)。

1ULL < 63unsigned long long シフトの前にシフトオペランドを作成するために使用します。

于 2012-05-08T13:15:31.613 に答える
6

C規格では、シフトが正しく機能する必要があります。特定のバグのあるコンパイラには、説明した欠陥がある可能性がありますが、それはバグのある動作です。

これはテストプログラムです:

#include <stdio.h>
#include <inttypes.h>

int main(void)
{
    uint64_t x = 1;
    for (int i = 0; i < 64; i++)
        printf("%2d: 0x%.16" PRIX64 "\n", i, (x << i));
    return 0;
}

これは、GCC4.1.2でRHEL5を実行しているi686マシン、x86 / 64マシン(RHEL5とGCC4.1.2も実行している)、およびx86 / 64 Mac(Mac OS X 10.7を実行している)での出力です。 3(GCC 4.7.0を使用)。これは期待どおりの結果であるため、32ビットマシンでは必要な問題はなく、GCC 4.1.2以降、GCCは少なくともそのようなバグを示していないと結論付けています(おそらくこのようなバグは発生していません)。

 0: 0x0000000000000001
 1: 0x0000000000000002
 2: 0x0000000000000004
 3: 0x0000000000000008
 4: 0x0000000000000010
 5: 0x0000000000000020
 6: 0x0000000000000040
 7: 0x0000000000000080
 8: 0x0000000000000100
 9: 0x0000000000000200
10: 0x0000000000000400
11: 0x0000000000000800
12: 0x0000000000001000
13: 0x0000000000002000
14: 0x0000000000004000
15: 0x0000000000008000
16: 0x0000000000010000
17: 0x0000000000020000
18: 0x0000000000040000
19: 0x0000000000080000
20: 0x0000000000100000
21: 0x0000000000200000
22: 0x0000000000400000
23: 0x0000000000800000
24: 0x0000000001000000
25: 0x0000000002000000
26: 0x0000000004000000
27: 0x0000000008000000
28: 0x0000000010000000
29: 0x0000000020000000
30: 0x0000000040000000
31: 0x0000000080000000
32: 0x0000000100000000
33: 0x0000000200000000
34: 0x0000000400000000
35: 0x0000000800000000
36: 0x0000001000000000
37: 0x0000002000000000
38: 0x0000004000000000
39: 0x0000008000000000
40: 0x0000010000000000
41: 0x0000020000000000
42: 0x0000040000000000
43: 0x0000080000000000
44: 0x0000100000000000
45: 0x0000200000000000
46: 0x0000400000000000
47: 0x0000800000000000
48: 0x0001000000000000
49: 0x0002000000000000
50: 0x0004000000000000
51: 0x0008000000000000
52: 0x0010000000000000
53: 0x0020000000000000
54: 0x0040000000000000
55: 0x0080000000000000
56: 0x0100000000000000
57: 0x0200000000000000
58: 0x0400000000000000
59: 0x0800000000000000
60: 0x1000000000000000
61: 0x2000000000000000
62: 0x4000000000000000
63: 0x8000000000000000
于 2012-05-08T13:18:09.067 に答える
4

Daniel Fischerの答えは、C言語の仕様に関する質問に答えます。可変量のシフトを発行したときにx86マシンで実際に何が起こるかについては、Intel Software Developer Manual Volume 2B、p。を参照してください。4-506:

カウントは5ビットにマスクされます(64ビットモードでREX.Wが使用されている場合は6ビット)。カウント範囲は0〜31(64ビットモードでREX.Wを使用する場合は63)に制限されます。

したがって、31ビットまたは63ビット(それぞれ32ビット値と64ビット値の場合)よりも大きい量だけシフトしようとすると、ハードウェアはシフト量の下位5ビットまたは6ビットのみを使用します。したがって、このコード:

uint32_t RightShift(uint32_t value, uint32_t count)
{
    return value >> count;
}

RightShift(2, 33) == 1x86およびx86-64になります。C標準によると、まだ未定義の動作sarですが、x86では、コンパイラーがそれを命令にコンパイルすると、そのアーキテクチャーで定義された動作になります。ただし、アーキテクチャ固有の癖に依存するこの種のコードを作成することは避けてください。

于 2012-05-08T17:12:56.067 に答える
2

0とタイプの幅の前身の間に含まれる数値でシフトしても、未定義の動作は発生しませんが、負の数を左シフトすると発生します。あなたはそれをしますか?

一方、負の数を右シフトすることは実装定義であり、ほとんどのコンパイラは、符号付き型を右シフトするときに、符号ビットを伝搬します。

于 2012-05-08T13:14:51.910 に答える
1

いいえ、大丈夫です。

ISO 9899:20116.5.7ビット単位のシフト演算子

右のオペランドの値が負であるか、プロモートされた左のオペランドの幅以上の場合、動作は未定義です。

ここではそうではないので、すべて問題なく明確に定義されています。

于 2012-05-08T13:20:39.567 に答える