2010-12-01 34 views
20

我想從浮點中提取位而不調用未定義的行爲。這是我的第一次嘗試:浮點數和嚴格的別名

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

我真的沒有看到一個問題的第一個方法 - 你甚至不用指向同一個對象的兩個指針。你應該沒問題,儘管你可能需要一個編譯時斷言sizeof(float)== sizeof(unsigned)。我也沒有看到聯盟黑客的問題(雖然我會再次驗證大小)。但我確信有一些我不知道的晦澀難懂的規則。讓我們坐下來等待人們證明我錯了! – EboMike 2010-12-01 19:45:58

+1

@Eomom:第一種方法違反了嚴格的鋸齒規則。閱讀這篇文章:http://cellperformance.beyond3d.com/articles/2006/06/understanding-strict-aliasing.html – Goz 2010-12-01 19:47:17

+0

謝謝,我知道有人會證明我錯了:) – EboMike 2010-12-01 19:48:15

回答

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

因爲你正在寫一個固定數量的編譯器會優化它。

這表示聯合方法得到了廣泛的支持。

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

當然,這種技術會產生特定於您機器字節排序的輸出。

6

工會黑客肯定是未定義的行爲,對不對?

是和否。根據標準,這絕對是未定義的行爲。但是這是一個常用的技巧,GCC和MSVC以及據我所知,其他所有流行的編譯器都明確保證它是安全的並且能夠按預期工作。

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