14

2 つの浮動小数点数が与えられた場合、2 つの値のいずれかがゼロ (+0.0 または -0.0) である場合、同じ符号を持っていると見なす必要があるため、それらが同じ符号を持っているかどうかを確認する効率的な方法を探しています。サイン

例えば、

  • SameSign(1.0, 2.0) は true を返す必要があります
  • SameSign(-1.0, -2.0) は true を返す必要があります
  • SameSign(-1.0, 2.0) は false を返す必要があります
  • SameSign(0.0, 1.0) は true を返す必要があります
  • SameSign(0.0, -1.0) は true を返す必要があります
  • SameSign(-0.0, 1.0) は true を返す必要があります
  • SameSign(-0.0, -1.0) は true を返す必要があります

SameSignC++での単純だが正しい実装は次のようになります。

bool SameSign(float a, float b)
{
    if (fabs(a) == 0.0f || fabs(b) == 0.0f)
        return true;

    return (a >= 0.0f) == (b >= 0.0f);
}

IEEE 浮動小数点モデルを想定すると、分岐のSameSignないコードにコンパイルされるバリアントを次に示します (少なくとも Visual C++ 2008 では)。

bool SameSign(float a, float b)
{
    int ia = binary_cast<int>(a);
    int ib = binary_cast<int>(b);

    int az = (ia & 0x7FFFFFFF) == 0;
    int bz = (ib & 0x7FFFFFFF) == 0;
    int ab = (ia ^ ib) >= 0;

    return (az | bz | ab) != 0;
}

次のようにbinary_cast定義されています。

template <typename Target, typename Source>
inline Target binary_cast(Source s)
{
    union
    {
        Source  m_source;
        Target  m_target;
    } u;
    u.m_source = s;
    return u.m_target;
}

私は2つのことを探しています:

  1. SameSignビット トリック、FPU トリック、さらには SSE 組み込み関数を使用した、より高速で効率的な の実装。

  2. SameSign3 つの値への効率的な拡張

編集:

SameSignの3つのバリアント(元の質問で説明されている2つのバリアントとスティーブンのバリアント)でパフォーマンス測定を行いました。各関数は、-1.0、-0.0、+0.0、および +1.0 でランダムに埋められた 101 個の float の配列内のすべての連続する値のペアに対して、200 ~ 400 回実行されました。各測定は 2000 回繰り返され、最小限の時間が維持されました (すべてのキャッシュ効果とシステムによるスローダウンを除外するため)。コードは、Visual C++ 2008 SP1 でコンパイルされ、最大限の最適化と SSE2 コード生成が有効になっています。測定は Core 2 Duo P8600 2.4 Ghz で行われました。

配列から入力値をフェッチし、関数を呼び出して結果を取得するオーバーヘッドをカウントしないタイミングを次に示します (これは 6 ~ 7 クロックティックになります)。

  • 単純なバリアント: 15 ティック
  • ビットマジックバリアント:13ティック
  • Stephens のバリアント: 6 ティック
4

3 に答える 3

20

無限大をサポートする必要がない場合は、次を使用できます。

inline bool SameSign(float a, float b) {
    return a*b >= 0.0f;
}

これは実際にはほとんどの最新のハードウェアでかなり高速であり、完全に移植可能です。ただし、(ゼロ、無限大)の場合は正しく機能しません。これは、ゼロ*無限大がNaNであり、符号に関係なく、比較がfalseを返すためです。また、aとbの両方が小さい場合、一部のハードウェアで異常なストールが発生します。

于 2010-05-27T16:23:13.457 に答える
5

おそらく次のようなものです:

inline bool same_sign(float a, float b) {
    return copysignf(a,b) == a;
}

コピーサインの詳細については、コピーサインのマニュアルページを参照してください(-0!= +0であることも確認してください)。

または、C99関数がある場合はこれ

inline bool same_sign(float a, float b) {
    return signbitf(a) == signbitf(b);
}

ちなみに、gccでは少なくともcopysignとsignbitの両方が組み込み関数であるため、高速である必要があります。組み込みバージョンが使用されていることを確認したい場合は、__ builtin_signbitf(a)を実行できます。

編集:これは3つの値の場合にも簡単に拡張できるはずです(実際には両方とも...)

inline bool same_sign(float a, float b, float c) {
    return copysignf(a,b) == a && copysignf(a,c) == a;
}

// trust the compiler to do common sub-expression elimination
inline bool same_sign(float a, float b, float c) {
    return signbitf(a) == signbitf(b) && signbitf(a) == signbitf(c);
}

// the manpages do not say that signbit returns 1 for negative... however
// if it does this should be good, (no branches for one thing...)
inline bool same_sign(float a, float b, float c) {
    int s = signbitf(a) + signbitf(b) + signbitf(c);
    return !s || s==3;
}
于 2010-06-14T15:43:52.990 に答える
0

signbit に関する注意事項: マクロは int を返し、man ページには、「x の値に符号ビットが設定されている場合、0 以外の値を返す」と記載されています。これは、signbitbool same_sign()が 2 つの異なる負の値に対して 2 つの異なる非ゼロの int を返す場合に、Spudd86 が機能することが保証されていないことを意味します。

最初に bool にキャストすると、正しい戻り値が保証されます。

inline bool same_sign(float a, float b) {
    return (bool)signbitf(a) == (bool)signbitf(b);
}
于 2015-06-10T07:11:38.637 に答える