5

コンパイル時に符号付き型を操作するときに右シフトが算術であるかどうかをチェックする最も移植性の高い方法は何でしょうか(たとえば、であるかどうか-2 >> 1) 。-1

私の考えは、コンパイル時にこれを何らかの方法でチェックし、これを検出できるようにすることです。これにより、関数のさまざまなバージョンをコンパイルできます(演算子>>が実際に算術シフトであるかどうかによって異なります)。

トピックを読んで、 C / C ++の符号付き右シフトが特定のコンパイラの算術であることを確認しますか?フラグを初期化するというアイデアになりました

static const bool is_arithmetic_rs = (((signed int)-1)>>1) == ((signed int)-1));

そして、次のように実行時にテストします。

if (is_arithmetic_rs) {
  // some fast algorithm using arithmetic right shifts (using >> operator)
} else {
  // the same algorithm without arithmetic right shifts (much slower)
}

ただし、可能であれば毎回この分岐を避けたいと思います。簡単にするために、ポータブル算術右シフトを実装したいとします。関数が呼び出されるたびにこれをチェックする必要がある場合、これはパフォーマンスに大きな影響を与えるため、可能であればコンパイル時にチェックしたいと思います。

このチェックを実行する移植可能な方法が存在しない場合、特定のコンパイラ/プラットフォームのifdefをチェックするなど、ベストエフォートベースでチェックすることによってこれを実行する方法はありますか?

4

7 に答える 7

8

このようなチェックを実行する最良の方法は、GNUautotoolsなどが行うことです。

  • ターゲットプラットフォームで小さなプログラムをコンパイルし、何が起こるかをテストします

  • #defineヘッダーファイルに適切なものを設定します

  • そのヘッダーファイルをソースファイルにインクルードします

  • #ifdef必要に応じて、適切に定義されたマクロを使用して、あらゆる小さなことに対するディレクティブでコードを乱雑にしないようにします。

  • メインプロジェクトをコンパイルします

そうすれば、サポートされている機能と、実際の各ハードウェアプラットフォームとオペレーティングシステムのさまざまな癖を備えたテーブルを作成する必要がなくなります。それらの組み合わせは言うまでもありません。ただし、ターゲット上にコードをビルドしない場合は、最初のステップを、ターゲット用に事前に提供されたテーブル/機能のリストに置き換える必要があります。

既存のマクロとプラットフォーム固有の情報を再利用し、独自のマクロを作成して車輪の再発明を行う必要をなくすために、GNUautotoolsやCMakeなどの広く使用されているビルドシステムを確認する必要があります。

ところで、最近のまともなコンパイラは、定数式を使用した単純なテストを最適化する必要があるため、必要に応じて(おそらくマクロを介して)ランタイムテストを使用しても、パフォーマンスがそれほど低下することはありません。コードをテストしてプロファイルを作成し、確認する必要があります。

于 2011-02-25T22:22:58.200 に答える
7

分岐は、前処理時間テストを使用して回避できます

#if ((-1)>>1) == (-1))
...
#else
...
#endif
于 2011-02-25T22:31:09.877 に答える
5

答えというよりは本当にコメントです(しかし、どうやら私は評判が悪いようです)

ここでの回答のいくつかは、次のようなプリプロセッサチェックを使用しています

#if ((-1)>>1) == (-1))

個人的には、プリプロセッサがコンパイラが生成するコードの種類を教えてくれるとは信じていません。

于 2012-03-09T13:16:07.997 に答える
3

コンパイラが利用可能なときに算術シフトへの除算を最適化しないことを実際に確認しましたか?

それ以外の場合は、テンプレートを使用できると思います。

template <bool B>
void do_work();

template <>
void do_work<true>()
{
    // Do stuff with H/W.
}

template <>
void do_work<false>()
{
    // Do slow stuff with S/W.
}

do_work<(-2 >> 1) == -1>();

次に、インライン関数で使用する方がきれいになります。

inline real_do_work()
{
    do_work<(-2 >> 1) == -1>();
}
于 2011-02-25T22:39:09.253 に答える
1

インスピレーションを得たジュゼッペR..の答え:

#if -2 >> 1 == -1
#define rshift_ar(X, Y) ((X) >> (Y))
#elif ~(~(-2) >> 1) == -1
#define rshift_ar(X, Y) ((X) >= 0 ? (X) >> (Y) : ~(~(X) >> (Y)))
#else
#error "unsupported shifting semantics"
#endif
于 2011-02-26T11:40:03.877 に答える
1

これを試して:

#define SAR(x,y) ((x)>=0) ? ((x)>>(y)) : (~(~(x)>>(y)))

優れたコンパイラは((x)>>(y))、CPU が正常であると仮定してこれを最適化します。

どのコンパイラが優れているかについてのフィードバックは大歓迎です。

于 2011-02-25T23:14:32.087 に答える
0

このプリプロセッサの魔法はすべて、適切なコンパイラでは役に立ちません。上記のコードが実際に出力に分岐を生成することを確認しましたか? static const boolcan はコンパイル時の定数として評価されるので、私はそれを非常に疑っていfalseます。こちらの私の回答も参照してください。if (is_arithmetic_rs)-O1

さらに、特に異なるシフトを持つプラットフォーム間でクロスコンパイルする場合、プリプロセッサの出力がコンパイラの出力と同じであることが保証されているとは思えません。

于 2012-09-14T07:50:25.483 に答える