2009-10-20 7 views
18

C/C++標準(see this link)によると、CおよびC++の>>演算子は、符号付き数値の算術シフトであるとは限りません。ビットが右にシフトされると、0(論理)または符号ビット(算術)がシフトインされるかどうかは、コンパイラの実装に依存します。C/C++ signed right shiftが特定のコンパイラの算術演算であることを確認しますか?

このコードは、符号付き整数の論理右シフトを実装するコンパイラのコンパイル時にASSERT(失敗)する機能を果たしますか?

#define COMPILE_TIME_ASSERT(EXP) \ 
    typedef int CompileTimeAssertType##__LINE__[(EXP) ? 1 : -1] 

#define RIGHT_SHIFT_IS_ARITHMETIC \ 
    ((((signed int)-1)>>1) == ((signed int)-1)) 

// SHR must be arithmetic to use this code 
COMPILE_TIME_ASSERT(RIGHT_SHIFT_IS_ARITHMETIC); 
+3

論理シフトを使用しているマシンを持っている人には、どのような処理が行われませんか?そのようなマシン/コンパイラであなたのソフトウェアが使えないのはなぜですか?符号付き数値の右シフトが算術式か論理式かにかかわらず動作するように、コードを書く方が良いでしょうか? –

+6

私はビットツイディリングを通じてブランチフリーセレクション(BFS)を使用しています。算術シフトが必要です。私はCOMPILE_TIME_ASSERT(RIGHT_SHIFT_IS_ARITHMETIC)を入れています。 BFSヘッダーに追加します。コードでは、RIGHT_SHIFT_IS_ARITHMETIC定義を使用して従来のパスまたはブランチのないパスを選択する必要があります。分岐予測ミスによるペナルティのために、PS3/XBOX360 CPUのブランチフリーコードを使用すると、大幅に高速化することができます。 – Adisak

+1

ところで、コンパイル時に失敗したコンパイルは、理由が明示的に記載されているところで、コードが不思議に失敗するよりも優れています...基本的には、これらのルーチンはこのコンパイラ(またはCPU)ではサポートされていません。 – Adisak

答えて

6

私によく見えます!アセンブリファイルを発行する(またはデバッガでコンパイルされたプログラムをロードする)ようにコンパイラを設定し、signed int i; i >> 1;のために発行するオペコードを調べることもできますが、それはあなたのソリューションのように自動ではありません。

符号付き数値の算術右シフトを実装していないコンパイラが見つかった場合は、それについて聞きたいと思います。

+0

ええ、私は基本的に自動的にしたい...オペコードを見ることは、他のチームがさまざまなプラットフォームで使用できるライブラリで使用しているので、この要件の合理的な期待ではありません。 – Adisak

+0

[UNISYS 2200システム](http://stackoverflow.com/a/12277974/995714)のコンパイラは、符号付きの型に対して論理右シフトを使用します。 E1 >> E2という表現の結果は、E1(ビットパターンとして解釈される)が右のE2ビット位置にシフトされ、右シフトがE1(すなわち、左にゼロで埋められる)式は符号付き整数型*です。 " http://public.support.unisys.com/2200/docs/cp14.0/pdf/78310422-011.pdf –

1

なぜアサートしますか?コンパイラのシフト演算子があなたのニーズに合っていない場合は、結果を符号拡張することで正常に修正できます。また、時には実行時に十分です。結局のところ、コンパイラのオプティマイザは、実行時のうち、コンパイル時間を作ることができます。shift_is_arithmetictrueであることが保証されている場合、価値があるすべてのコンパイラはその塩がなくなりますので

static const bool
template <typename Number> 
inline Number shift_logical_right(Number value, size_t bits) 
{ 
    static const bool shift_is_arithmetic = (Number(-1) >> 1) == Number(-1); 
    const bool negative = value < 0; 
    value >>= bits; 
    if (!shift_is_arithmetic && negative) // sign extend 
     value |= -(Number(1) << (sizeof(Number) * 8 - bits)); 
} 

は、コンパイル時に評価することができます全体がifであり、構成がconst bool negativeであり、デッドコードである。

注:コードはMonoのencode_sleb128関数から適合されます。here

更新

あなたが本当に算術シフトせずにマシン上でコンパイルを中止したい場合は、まだプリプロセッサに頼らない方がいいでしょう。あなたがstatic_assert(またはBOOST_STATIC_ASSERT)を使用することができます。

static_assert((Number(-1) >> 1) == Number(-1), "Arithmetic shift unsupported."); 
+0

意味があるだけのコードが多数あります(たとえば、「マスクを使用したブランチフリー選択」など)算術符号付きシフトを持つコンパイラで使用する。それらの算術シフトを正常にエミュレートすると、おそらく元のコードよりも大幅に遅くなるため、「最適化」が不要になります。 – Adisak

+0

十分に公正。それにもかかわらず、2番目の点はまだ有効です。プリプロセッサに頼らないでください。更新を参照してください。 – marton78

+1

static_assert()はC++ 11xで、boostを使用することはできません。しかし、私が行っていたチェックはプリプロセッサに依存していないので、次のようにしても構いませんが、私が書いた例よりも読んで理解する方がはるかに難しいです: 'typedef int CompileTimeAssertArithmeticShift [(((signed int)-1) >> 1)==((signed int)-1))? 1:-1]; ' – Adisak

0

あなたの様々な意見からは、このクロスプラットフォームを使用してについて話しています。コンパイラがプラットフォーム用にコンパイルするとき、コンパイル時の演算子は実行時のものと同じように動作することをコンパイラが保証していることを確認してください。

異なる動作の例は、浮動小数点数で見つけることができます。 intにキャストしている場合は、コンパイラは定数式、倍精度、または拡張精度で定数式の演算を行っていますか?例えば、

constexpr int a = 41; constexpr int b =(a/7.5);

私が言っていることは、非常に多くの異なるアーキテクチャを扱っているときに、コンパイル時と同じ動作をコンパイル時と同じように保証することです。

コンパイラが内部的に符号拡張し、ターゲット上に目的のオペコードを生成しない可能性があります。実行時にテストするか、アセンブリ出力を確認するしかありません。

アセンブリの出力を見るのは世界の終わりではありません...いくつのプラットフォームがありますか?これはパフォーマンスが非常に重要なので、5つの異なるアーキテクチャーのアセンブラ出力を1〜3行表示するという「仕事」を行うだけです。まるであなたのラインを見つけるためにアセンブリー出力全体(通常は!)を飛び越えなければならないかのようなものではありません。非常に簡単です。

関連する問題