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 を返す必要があります
SameSign
C++での単純だが正しい実装は次のようになります。
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つのことを探しています:
SameSign
ビット トリック、FPU トリック、さらには SSE 組み込み関数を使用した、より高速で効率的な の実装。SameSign
3 つの値への効率的な拡張。
編集:
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 ティック