2010-12-01 9 views
20

未定義のビヘイビアを呼び出さずに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ビット幅であると仮定)フロートからビットを抽出することができますか?


編集:そしてここには、Gozによって提案されたmemcpyバージョンがあります。多くのコンパイラは、まだ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; 
} 
+0

私は本当に非常に最初のアプローチの問題が表示されていないさまざまな種類のアクセス左辺値のない使用がないので、次は、エイリアシング規則に違反しない - あなたも持っていませんが2つのポインターが同じオブジェクトを指しています。コンパイル時にsizeof(float)== sizeof(unsigned)をアサートすることが望ましいかもしれませんが、うまくいくはずです。私はまた、(私は再びサイズを確認するだろうが)組合のハックに問題が表示されません。しかし、私は気づいていないいくつかのあいまいなルールがあると確信しています。座って、人々が私を間違っていると証明するのを待ちましょう! – EboMike

+1

@Ebomike:最初の方法は、厳密なエイリアシング規則に反します。これを読んでください:http://cellperformance.beyond3d.com/articles/2006/06/understanding-strict-aliasing.html – Goz

+0

ありがとう、私は誰かが私が間違っていることを証明するだろうと知っていた:) – EboMike

答えて

16

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するので、コンパイラはそれを最適化します。

これは、ユニオンメソッドが非常に広くサポートされていると言いました。

+0

私は、実際には、unionメソッドをサポートしていないコンパイラにはバグを報告すると言っています。はい、それは技術的には標準の一部ではありませんが、組み込みプログラミングで広く使われているため、それをサポートしていないコンパイラはそれほど有用ではありません。 – Crashworks

+0

@FredOverflow ... typo;)修正されました。 – Goz

+0

@Crashworks:あなたはバグを報告してもいいです...コンパイラライターがサルを与えなければならないというわけではありません;)彼らのコンパイラは依然として完全に準拠することができます。 – Goz

0

あなたが本当にfloat型のサイズについてとらわれないことと、単なる生のビットを返し、このような何かをしたい場合:

float a = 12345.6789; 
char buffer[sizeof(float)]; 

float_to_bytes(buffer, a); 

void float_to_bytes(char *buffer, float f) { 
    union { 
     float x; 
     char b[sizeof(float)]; 
    }; 

    x = f; 
    memcpy(buffer, b, sizeof(float)); 
} 

そして、そのようにそれを呼び出しますもちろん、このテクニックはあなたのマシンのバイトオーダーに特有の出力を生成します。

6

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

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

+0

興味のある - その部分は未定義の動作ですか? (浮動小数点を整数として誤解しているのを除いて) – EboMike

+4

それは許されません。ユニオンの1つのメンバーだけが一度に「アクティブ」です。構造体のメンバに書き込むと、同じメンバから読み取ることのみが許可されます。他のメンバーを読み取った結果は未定義です。 – jalf

+2

@EboMike "以外" ..それはまさにUBと同じです。ユニオンのアクティブなメンバーと互換性のあるエイリアスではないメンバーから読み込むことは、エイリアス違反です。以下は例です: 'union A {int a;符号なしchar b; }; A x = {10};あなたが 'unsigned char'型の左辺値によって' int'にアクセスすることが許されているので、 'x.b;'を返します。 –

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); 
} 
+0

これは標準が壊れていることを証明します。 temporary.memberが左辺値でないことはばかげている。私はstdの人が "価値"(価値のような)と "価値"(一時的)という用語で混乱していると思います。 Lol – curiousguy

+1

@Johannes:この推論はまだ本当ですか? 'b'にアクセスすると、ユニオンの非アクティブなメンバーにアクセスしています。 – GManNickG

関連する問題