3

uint64_t 値を 64 ビット以上右にシフトすると、異常な結果と思われるものを発見しました。次のコードが値 0 を出力することを期待していましたが、そうではなく、値>>2 を出力しますが、g++ でのみです。

#include <iostream>
#include <cstdint>
using namespace std;
int main(void)
{
    uint64_t value = 0x5d4d629e80d5489UL;
    int shift = 66;
    cout << hex << (value >> shift) << endl;
    return 0;
}

私がコンパイルして実行するもの:

$ g++ -std=c++0x mad.cpp
$ ./a.out
175358a7a035522
$ uname -a
Linux svr 3.2.0-26-generic #41-Ubuntu SMP Thu Jun 14 17:49:24 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux
$ g++ --version 
g++ (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3

g++ (Debian 4.4.5-8) 4.4.5 を搭載した別の Linux ボックスでも同じ動作をします。ただし、これを Visual Studio C++ (Express Edition) でコンパイルすると、同じ動作が得られません。最初に期待していた結果、つまり 0 が得られます。

x と y が序数で、y が x のビット数よりも大きい場合、C++ の x>>y は 0 になると仮定しても安全かどうかを知りたいです。これは g++ のバグですか、それともここで準拠していないのは Visual C++ ですか? これは未定義の動作ではない/すべきではないと思いますか? これはCPU固有でしょうか?

8*sizeof(value) を超えるすべてのシフトをいつでも明示的にチェックできることを認識していますが、これにより、パフォーマンスが重要なコードで回避したい追加のブランチが導入されます。

これは既知の問題ですか? もしそうなら、一貫したクロスプラットフォーム評価を視野に入れた推奨戦略は何ですか?

4

3 に答える 3

9

シフト演算子に関する C++ 標準から:

右オペランドが昇格された左オペランドのビット単位の長さ以上である場合、動作は未定義です。

于 2012-07-16T00:54:20.123 に答える
1

この動作は未定義です。
あまりにも多くのビットによるシフトがどのように不正であるか、およびそれから身を守る方法についてのいくつかの読書を検討してください: INT34-C. 負の数のビットまたはオペランドに存在するよりも多くのビットをシフトしないでください

于 2012-07-16T00:57:12.337 に答える
0

未定義の動作の理由の 1 つは、シフトの実装が異なる基盤となるハードウェアに依存することです。

オリジナルの 8086 では、位置ごとに 1 クロック ティックを使用して、要求された回数だけシフトが実行されました。これにより、保証された割り込み応答時間が、お粗末になることがすぐに判明しました。

そのため、286 以降、ハードウェアはシフト カウントをマスクし、下位ビットのみを使用します。これは、66 が 2 ポジション シフトになったときに表示されるものです。

于 2012-07-16T08:00:30.487 に答える