2013-10-28 8 views
30

私は絶対にここで狂っている必要がありますが、私のマシン上のgcc 4.7.3は最も不条理な結果を与えています。ここで私はテストだ正確なコードです:算術右シフトは偽の結果をもたらしますか?

#include <iostream> 

using namespace std; 

int main(){ 
    unsigned int b = 100000; 
    cout << (b>>b) << endl; 
    b = b >> b; 
    cout << b << endl; 
    b >>= b; 
    cout << b << endl; 
    return 0; 
} 

は今、右にそれ自体でシフトしています任意の数は、(整数除算n>1、そして正/符号なしとn/(2^n) == 0)を生じるはずです何とかここに私の出力です:

100000 
100000 
100000 

私はクレイジーですか?おそらく何が起こっているのでしょうか?

+0

@ShafikYaghmour:これはコンパイラを前提としています指示をすることさえ気にする。このプログラムを拒否する権利は十分です。 – MSalters

+0

@MSalters確かに、現時点ではコンパイラ/プラットフォーム/バージョン固有のものになっていますが、現在と最近のバージョンではこれが当てはまります。すでに述べたように、あなた自身がはっきりしているので、 'gcc'は'-O0'を使うとき' shr'を生成します。 –

+0

@ShafikYaghmour:Intelはgccがサポートする多くのプラットフォームの1つに過ぎず、最適化の段階が異なります。最適化の一般的なやり方は、「この値はシフトで使用されるため0から31の間でなければなりません。コードパスXに従うと値が0から31にならないのでコードパスXは不可能です私はそれについての指示を生成する必要さえもありません。 GCCは、ヌルポインタチェックのためにこれを実行します。 – MSalters

答えて

44

Cと同様に、シフトはシフトされた値のサイズ(ビット単位)に制限されます。たとえば、unsigned intが32ビットの場合、31より大きいシフトは未定義です。

実際には、シフト量の5つの最下位ビットが使用され、上位ビットは無視されるという共通の結果が得られます。これはコンパイラがマシン命令を生成するためです(例えば、x86ではSHR)。

この場合、シフト値は100000(10進数)で、11000011010100000が2進数である - 下位5ビットはゼロです。つまり、あなたは効果的にシフトを0にしています。しかし、これに頼るべきではありません。技術的には、あなたが見ているのは、未定義の動作です。

参照:Cについて

N1570セクション6.5.7:

右オペランドの値が負であるか、より大きいかまたは促進左オペランドの幅に等しい ある場合、動作は 未定義です。 C++、N3690セクション5.8「[expr.shift]」について

右オペランドが負、以上 よりやビット長に等しい場合の動作は未定義です左オペランドを宣言しました。

N1570は、リリースされたISO C11標準とほぼ同じです。この節は、1989年のANSI C標準以来ほぼ同じです。

N3690は、C++標準の最近のドラフトです。私はそれが最高のものかどうかは分かりませんが、この節は変更されていません。あなたは左オペランドのビット長よりも大きくシフトしている場合はundefined behaviorを呼び出している

+1

異なるコンパイラでこの動作を観察しましたか? –

+3

@ GrijeshChauhan、これはCおよびC++の標準仕様で記述されています。 SHR命令の生成に関して、私はこれを観察した。 – davmac

+0

聖母のコーディング...私はちょうどこれをテストし、100000のバイナリは '0b110000110101_00000'です。 – Suedocode

31

draft C++ standardセクション5.8シフト演算子段落が言う(強調鉱山):

オペランドはなら整数型またはスコープなしの列挙型であり、積極的なプロモーションが実行されます。結果のタイプは、プロモートされた左オペランドのタイプです。 右のオペランドが負の場合、または昇格された左のオペランドのビットの長さ以上の場合、動作は未定義です。

cout << (b>> 100000) ; 

又はbCONSTされている場合は、:gccclang両方がリテラル場合、シフト量ならば、このコードの警告を生成することができることに注意することは興味深い

gccの警告は次のとおりです。

warning: right shift count >= width of type [enabled by default] 

MSaltersは、質問へのコメントで指摘するように、我々は標準と一致している、これは未定義の動作であることからも、この警告に頼ることができない場合がありは的に未定義の動作に注意し、

注:言う定義セクション[...]許容未定義の動作を予測できない結果と完全に状況を無視してから、環境の文書化された方法の特性に変換またはプログラムの実行中に振舞うの範囲にある(とや診断メッセージの発行なしに)、翻訳を終了すること(診断メッセージの発行を伴って)実行することができる。 [...]

プラットフォームの特定の詳細

は、例えば、コードのシフトの見かけの欠如のための潜在的な説明があるため、いくつかのプラットフォーム上のシフト・カウントは5 bitsにををマスクすることができます

x86アーキテクチャ上で例えば我々はIA-32アーキテクチャにおける互換性セクションが言うに Intel® 64 and IA-32 Architectures Software Developer’s Manualセクション SAL/SAR/SHL/SHR-Shiftキーを見ることができます

8086はシフトカウントをマスクしません。しかし、他のすべてのIA-32プロセッサ(インテル286プロセッサで始まる)はシフト・カウントを5ビットにマスクし、最大数は31です。[...]

+0

80286は意図的にワードサイズまでのシフト量を許可しています。私は80386が(オペランドサイズが32ビットのときに6ビットのCLを使って)同様にしないのはなぜだろうか? – supercat

+0

x86の詳細については、[このブログの投稿](https://david.wragg.org/blog/2012/11/shift-instructions.html)も参照してください。 –

関連する問題