フロートビットと厳格なエイリアシング
-
29-09-2019 - |
質問
未定義の動作を呼び出すことなく、フロートからビットを抽出しようとしています。これが私の最初の試みです:
unsigned foo(float x)
{
unsigned* u = (unsigned*)&x;
return *u;
}
私が理解しているように、これは厳格なエイリアスルールのために機能することを保証されていませんよね?キャラクターポインターで中間ステップを踏むと機能しますか?
unsigned bar(float x)
{
char* c = (char*)&x;
unsigned* u = (unsigned*)c;
return *u;
}
それとも、個々のバイトを自分で抽出する必要がありますか?
unsigned baz(float x)
{
unsigned char* c = (unsigned char*)&x;
return c[0] | c[1] << 8 | c[2] << 16 | c[3] << 24;
}
もちろん、これにはエンディアンネスに依存するという不利な点がありますが、私はそれと一緒に暮らすことができます。
ユニオンハックは間違いなく未定義の行動ですよね?
unsigned uni(float x)
{
union { float f; unsigned u; };
f = x;
return u;
}
完全に、こちらの参照バージョンがあります foo
. 。また、未定義の動作もそうですか?
unsigned ref(float x)
{
return (unsigned&)x;
}
したがって、フロートからビットを抽出することは可能ですか(両方が幅32ビットであると仮定します, 、 もちろん)?
編集:そしてこれがあります memcpy
Gozが提案したバージョン。多くのコンパイラがサポートしていないためです static_assert
それでも、私は交換しました static_assert
いくつかのテンプレートメタプログラムで:
template <bool, typename T>
struct requirement;
template <typename T>
struct requirement<true, T>
{
typedef T type;
};
unsigned bits(float x)
{
requirement<sizeof(unsigned)==sizeof(float), unsigned>::type u;
memcpy(&u, &x, sizeof u);
return u;
}
解決
真に問題を回避する唯一の方法は、Memcpyです。
unsigned int FloatToInt( float f )
{
static_assert( sizeof( float ) == sizeof( unsigned int ), "Sizes must match" );
unsigned int ret;
memcpy( &ret, &f, sizeof( float ) );
return ret;
}
あなたは固定金額をmemcpyingにしているため、コンパイラはそれを最適化します。
それは、ユニオンの方法が非常に広くサポートされていると言っています。
他のヒント
ユニオンハックは間違いなく未定義の行動ですよね?
はいといいえ。基準によれば、それは間違いなく未定義の動作です。しかし、GCCとMSVCが一般的に使用されているトリックであり、私が知る限り、他のすべての人気のあるコンパイラは、それが安全であることを明示的に保証します。
以下はエイリアシングルールに違反していません。
template<typename B, typename A>
B noalias_cast(A a) {
union N {
A a;
B b;
N(A a):a(a) { }
};
return N(a).b;
}
unsigned bar(float x) {
return noalias_cast<unsigned>(x);
}
フロートタイプのサイズについて不可知論者になりたい場合、生のビットを返したい場合は、次のようなことをします。
void float_to_bytes(char *buffer, float f) {
union {
float x;
char b[sizeof(float)];
};
x = f;
memcpy(buffer, b, sizeof(float));
}
次に、そう呼びます:
float a = 12345.6789;
char buffer[sizeof(float)];
float_to_bytes(buffer, a);
もちろん、この手法は、マシンのバイト順序に固有の出力を生成します。