[英]Setting bits in a double and compiling with optimization flags of g++
我試圖將位設置為雙精度(IEEE標准754)。 說我想'構建'一個3,我會設置雙浮點表示的第51位和第62位,這樣我得到二進制1.1 * 2,十進制是3.我寫了這個簡單的主:
int main() {
double t;
uint64_t *i = reinterpret_cast<uint64_t*>(&t);
uint64_t one = 1;
*i = ((one << 51) | (one << 62));
std::cout << sizeof(uint64_t) << " " << sizeof(uint64_t*) << " "
<< sizeof(double) << " " << sizeof(double*) << std::endl;
std::cout << t << std::endl;
return 0;
}
這個的輸出是
8 8 8 8
3
用g ++ 4.3編譯時沒有優化。 但是,如果我添加-O2或-O3優化標志,我會得到一個奇怪的行為。 也就是說,如果我只是按原樣離開主,我得到相同的輸出。 但是如果我刪除輸出4 sizeof的行,那么我得到的輸出
0
沒有sizeof輸出的未優化版本也會正確返回3。
所以我想知道這是否是優化器的錯誤,或者我在這里做錯了什么。
是的,您違反了該語言的別名規則。 不允許通過指向另一種類型的指針寫入一種類型的對象( char*
有一些例外)。
由於您從未寫入代碼中的任何double
,因此允許編譯器假定t
永遠不會賦值。 (並輸出錯誤本身:-)
GCC有一個擴展,允許你寫一個類型的值,並將它作為另一種類型讀取,如果你把它們都放在一個聯合中。 這是編譯器特定的(但是半便攜式,因為其他人必須遵循領先)。
從技術上講,你有未定義的行為,雖然顯然是標准的意圖,這在明顯的情況下起作用,如果可以看到reinterpret_cast
,那么打破它的編譯器是不正常的。 如果您知道平台的字節順序,則可以通過使用uint8_t
(字符類型)來操作位,或者將memcpy
存入uint64_t
,然后將結果memcpy
返回到double
。
如果你使用union
g ++將使這個工作。 在所有訪問都通過union類型的情況下。 然而,該標准明確禁止這一點(盡管它是標准前幾天的首選解決方案),而且我使用的編譯器不起作用。
使用g ++,還有一個選項-fnostrict-aliasing
,它可以使它工作。
嘗試:
int main()
{
volatile double t;
// ^^^^^^^^ Tells the compiler this may be modified unexpectedly.
volatile uint64_t& i = reinterpret_cast<volatile uint64_t&>(t);
uint64_t one = 1;
i = ((one << 51) | (one << 62));
std::cout << t << std::endl;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.