2017-09-15 836 views
-2

我想它在一個uint64_t中移動0xFF的3個字節的左,存儲,這應該是這樣工作的:爲什麼將0xff左移24位會導致錯誤的值(C++)?

uint64_t temp = 0xff << 24;

這就產生了0xffffffffff000000 的值是最肯定不是預期的0xff000000

但是,如果我移動它少於3個字節,它會導致正確的答案。

此外,嘗試將0x01左移3個字節確實有效。

這裏是我的輸出: 0xff shifted by 0 bytes: 0xff 0x01 shifted by 0 bytes: 0x1 0xff shifted by 1 bytes: 0xff00 0x01 shifted by 1 bytes: 0x100 0xff shifted by 2 bytes: 0xff0000 0x01 shifted by 2 bytes: 0x10000 0xff shifted by 3 bytes: 0xffffffffff000000 0x01 shifted by 3 bytes: 0x1000000

有了一些實驗,左移工作到3位爲每uint64_t中達到0x7f,這將產生0x7f000000。 0x80產生0xffffffff80000000。

有沒有人有這種奇怪的行爲的解釋? 0xff000000肯定落在uint64_t的2^64-1範圍內。

+2

'0xff'是一個'int',它通常是一個32位整數。所以'0xff << 24'在32位整數上進行數學運算,而不是在'uint64_t'上。所以最左邊的'1'位被當作符號位來處理,所以當我們將寬度擴展到64位整數時,我們將所有新位設置爲'1',當我們轉換成所有'f'時一個無符號整數 – Justin

+0

「奇怪」,如「我不明白它」?這種行爲的原因是所有這些常量都具有類型「int」。爲了得到你要查找的行爲,要麼確保它們是64位寬,要麼將它們明確寫爲無符號long long值(例如0xffULL),要麼將其轉換爲'std :: uint64_t'。 –

+1

嘗試'uint64_t temp = 0xffu << 24;'或'uint64_t temp = 0xfful << 24;' – gurka

回答

2

我懷疑這種行爲是依賴於編譯器的,但我看到了同樣的事情。

修復很簡單。確保在執行轉換之前將0xff轉換爲uint64_t類型。這樣編譯器將把它作爲正確的類型處理。

uint64_t temp = uint64_t(0xff) << 24 
0

左移產生一個負數(32位),然後填充到64位。

嘗試

0xff << 24LL; 
+0

右移操作數的類型不影響結果的類型;使用'24LL'與在這裏使用'24'沒有區別。換擋操作員在這方面與所有其他操作員不同。對於其他算術和邏輯運算符,較小的類型被提升爲匹配較大的類型。對於移位操作符來說,唯一可能被提升的參數是左邊的參數,只有當它是'char','short'或者它們的無符號變量時。 –

1

沒有人有這種怪異的行爲的解釋?

是,操作類型總是依賴於操作的類型和永遠的結果類型:

double r = 1.0/2.0; 
    // double divided by double and result double assigned to r 
    // r == 0.5 

double r = 1.0/2; 
    // 2 converted to double, double divided by double and result double assigned to r 
    // r == 0.5 

double r = 1/2; 
    // int divided by int, result int converted to double and assigned to r 
    // r == 0.0 

當你理解和remenber這一點,你就不會再掉這個錯誤。

0

讓我們把你的問題分成兩部分。第一種是移位操作,另一種是轉換爲uint64_t

就左移而言,您在32位(或更小)體系結構上調用未定義的行爲。正如其他人所提到的,操作數是int。具有給定值的32位int將是0x000000ff。請注意,這是一個有符號的數字,所以最左邊的位是符號。根據標準,如果你的移位影響符號位,結果是不確定的。這取決於實現的奇思妙想,它隨時可能發生變化,如果編譯器在編譯時識別它,它甚至可以完全優化。後者是不現實的,但它實際上是允許的。雖然你不應該依賴這種形式的代碼,但這實際上並不是困擾你的行爲的根源。

現在,第二部分。左移操作的未定義結果必須轉換爲uint64_t。該標準規定有符號到無符號整數的轉換:

如果目標類型是無符號的,所得到的值等於源值模2 N的最小無符號值,其中n是用於表示目的地的位的數目類型。

也就是說,根據目標類型是更寬還是更窄,有符號整數是符號擴展[腳註1]或截斷和無符號整數分別被零擴展或截斷。

該腳註闡明瞭符號擴展僅適用於當前在每個平臺上使用C++編譯器的二進制補碼錶示。

符號擴展意味着目標變量上符號位的所有內容都將填充符號位,這會在結果中生成所有f。正如你所指出的,你可以在不發生這種情況下將0x7f左移3個字節,這是因爲0x7f=0b01111111。轉換後,您得到0x7f000000這是最大的帶符號整數,即不影響符號位的最大數量。因此,在轉換中,擴展了0

正如其他人已經指出的那樣,在轉換操作之前轉換兩個參數之一解決了這個問題。

uint64_t temp = uint64_t(0xff) << 24 
相關問題