27

以下のコードは、いくつかのビット ハックによって高速な逆平方根演算を実行します。このアルゴリズムはおそらく 1990 年代初頭に Silicon Graphics によって開発され、Quake 3 にも登場しました。 より詳しい情報

ただし、 GCC C++ コンパイラから次の警告が表示さます。

static_castそのような状況では、reinterpret_castまたはdynamic_cast代わりに使用する必要がありますか?

float InverseSquareRoot(float x)
{
    float xhalf = 0.5f*x;
    int32_t i = *(int32_t*)&x;
    i = 0x5f3759df - (i>>1);
    x = *(float*)&i;
    x = x*(1.5f - xhalf*x*x);
    return x;
}
4

8 に答える 8

38

キャストを忘れてください。を使用しmemcpyます。

float xhalf = 0.5f*x;
uint32_t i;
assert(sizeof(x) == sizeof(i));
std::memcpy(&i, &x, sizeof(i));
i = 0x5f375a86 - (i>>1);
std::memcpy(&x, &i, sizeof(i));
x = x*(1.5f - xhalf*x*x);
return x;

元のコードは、最初にポインターを介してオブジェクトにint32_tアクセスすることによってを初期化しようとしますが、ここで規則が破られています。C スタイルのキャストは a と同等であるため、に変更しても大きな違いはありません。floatint32_treinterpret_castreinterpret_cast

memcpy を使用する場合の重要な違いは、バイトが から にコピーされるfloatことですint32_tが、floatオブジェクトはint32_t左辺値を介してアクセスされることはありません。これmemcpyは、void へのポインターを取得し、その内部が「魔法のよう」であり、エイリアシング ルールを破らないためです。

于 2013-07-22T14:23:02.497 に答える
13

ここには、タイプパニングの問題に対処するいくつかの良い答えがあります。

「高速逆平方根」の部分に取り組みたいと思います。最新のプロセッサでは、この「トリック」を使用しないでください。すべての主流のベクトル ISA には、高速な逆平方根を提供する専用のハードウェア命令があります。それらのすべては、この頻繁にコピーされる小さなハックよりも高速で正確です.

これらの命令はすべて組み込み関数を介して利用できるため、比較的使いやすいです。SSE では、rsqrtss(intrinsic: _mm_rsqrt_ss( ));を使用します。NEON で使用したいvrsqrte(intrinsic: vrsqrte_f32( )); そしてAltiVecではあなたが使いたいfrsqrte. ほとんどの GPU ISA には同様の命令があります。これらの推定値は、同じ Newton 反復を使用して精緻化できます。NEON には、vrsqrts定数をロードする必要なく、1 つの命令で精緻化の一部を実行する命令さえあります。

于 2013-08-11T01:46:53.773 に答える
4

C++20以降にアクセスできる場合は、使用できますstd::bit_cast

float InverseSquareRoot(float x)
{
    float xhalf = 0.5f*x;
    int32_t i = std::bit_cast<int32_t>(x);
    i = 0x5f3759df - (i>>1);
    x = std::bit_cast<float>(i);
    x = x*(1.5f - xhalf*x*x);
    return x;
}

現時点std::bit_castでは、MSVC でのみサポートされています。Godbolt でデモを見る

実装を待っている間、Clang を使用している場合は を試すことができます__builtin_bit_cast。このようにキャストを変更するだけです

int32_t i = __builtin_bit_cast(std::int32_t, x);
x = __builtin_bit_cast(float, i);

デモ

于 2020-11-14T08:14:44.440 に答える
1

型のパニングと厳密なエイリアシングの詳細については、こちらをご覧ください。

配列への型の唯一の安全なキャストは、配列へのキャストですchar。1 つのデータ アドレスを異なるタイプに切り替え可能にする場合は、union

于 2013-07-22T14:39:53.840 に答える
0

ここで機能する唯一のキャストはreinterpret_cast. (それでも、少なくとも 1 つのコンパイラは、動作しないことを保証するために最善を尽くします。)

しかし、あなたは実際に何をしようとしていますか?型のパニングを含まない、より良い解決策が確かにあります。型パニングが適切なケースは非常にまれであり、それらはすべて、シリアライゼーションや C 標準ライブラリの実装など、非常に低レベルのコードで行われます (例: のような関数 modf)。それ以外の場合 (おそらくシリアル化でも)、ldexpandのような関数modfはおそらくより適切に機能し、確実に読みやすくなります。

于 2013-07-22T14:27:15.263 に答える