23

未定義の動作を呼び出さずに float からビットを抽出しようとしています。これが私の最初の試みです:

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;
}
4

4 に答える 4

17

問題を完全に回避する唯一の方法は、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 しているため、コンパイラはそれを最適化します。

つまり、union メソッドは非常に広くサポートされています。

于 2010-12-01T19:45:47.207 に答える
6

ユニオンハックは間違いなく未定義の動作ですよね?

はいといいえ。標準によると、これは間違いなく未定義の動作です。しかし、GCC や MSVC、そして私が知る限り、他のすべての一般的なコンパイラは、安全で期待どおりに動作することを明示的に保証する、非常に一般的に使用されるトリックです。

于 2010-12-01T21:34:14.360 に答える
5

以下は、別の型にアクセスする左辺値を使用していないため、エイリアシング規則に違反していません。

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);
}
于 2011-02-08T16:37:31.563 に答える
0

floatタイプのサイズにとらわれず、生のビットを返すだけの場合は、次のようにします。

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);

もちろん、この手法では、マシンのバイト順序に固有の出力が生成されます。

于 2010-12-01T20:48:25.903 に答える