![](/img/trans.png)
[英]Rotate left and back to the right for sign extension with (signed short) cast in C
[英]Inconsistencies in sign extension when shifting signed int vs short
int main(){
signed int a = 0b00000000001111111111111111111111;
signed int b = (a << 10) >> 10;
// b is: 0b11111111111111111111111111111111
signed short c = 0b0000000000111111;
signed short d = (c << 10) >> 10;
// d is: 0b111111
return 0;
}
假設int
是 32 位,而short
是 16 位,
為什么b
會擴展符號而d
不會擴展符號? 我已經在 x64 上用 gdb 測試了這個,用 gcc 編譯。
為了擴展short
符號,我不得不使用兩個單獨的變量,如下所示:
signed short f = c << 10;
signed short g = f >> 10;
// g is: 0b1111111111111111
在signed short
的情況下,當在表達式中使用小於int
的 integer 類型時,它(在大多數情況下)被提升為int
類型。 這在C 標准的第 6.3.1.1p2 節中有詳細說明:
可以在可以使用
int
或unsigned int
的表達式中使用以下內容
- object 或具有 integer 類型(除
int
或unsigned int
之外)的表達式,其 integer 轉換等級小於或等於int
的unsigned int
等級。_Bool
、int
、signed int
或unsigned int
類型的位域。如果
int
可以表示原始類型的所有值(受寬度限制,對於位域),則該值將轉換為int
; 否則,它將轉換為unsigned int
。 這些被稱為integer 促銷活動 integer 促銷活動不會改變所有其他類型
這種提升特別發生在第 6.5.7p3 節中指定的按位移位運算符的情況下:
integer 提升在每個操作數上執行。 結果的類型是提升的左操作數的類型。 如果右操作數的值為負數或大於或等於提升的左操作數的寬度,則行為未定義。
因此將short
值 0x003f 提升為int
值 0x0000003f 並應用左移。 這導致 0x0000fc00,右移得到 0x0000003f 的結果。
帶signed int
案例更有趣一些。 在這種情況下,您將值 1 的位左移到符號位。 這會根據 6.5.7p4 觸發未定義的行為:
E1 << E2
的結果是E1
左移E2
位位置; 空出的位用零填充。 如果E1
具有無符號類型,則結果的值是E1×2 E2 ,比結果類型中可表示的最大值多模一減少。 如果E1
有帶符號類型和非負值,並且E1×2 E2在結果類型中是可表示的,那么這就是結果值; 否則,行為未定義。
因此,雖然 output 得到的signed int
案例是您可能期望的,但它實際上是未定義的行為,因此您不能依賴該結果。
integer 促銷活動根據 C 2018 6.5.7 3 自動將short
轉換為int
:
integer 提升在每個操作數上執行……
因此(c << 10)
將int
0b111111
向左移動 10 位,產生(在您的 C 實現中)32 位int
0b00000000000000001111110000000000。 其中的符號位為零; 這是一個正數。
當您signed short f = c << 10;
, c << 10
的結果太大而無法放入有signed short
。 它是 64,512,高於您的signed short
可以代表的最大值 32,767。 在賦值中,值被轉換為左操作數的類型。 根據 C 2018 6.3.1.3 3,轉換是實現定義的。 GCC 將此轉換定義為以 65,536 為模(類型中位數的兩倍)。 所以轉換 64,512 得到 64,512 − 65,536 = −1024。 所以f
設置為 -1024。
然后,在f >> 10
中,您正在移動一個負值。 作為有signed short
, f
仍被提升為int
,但這種轉換保留了該值,導致int
值為 -1024。 然后轉移。 此移位是實現定義的,並且GCC 將其定義為使用符號擴展移位。 所以-1024 >> 10
的結果是 -1。
對於符合 C 標准的初學者(6.5.7 位移位運算符)
3 對每個操作數執行 integer 提升。 結果的類型是提升的左操作數的類型。
因此這個值
signed short c = 0b0000000000111111;
在此聲明中使用的表達式中
signed short d = (c << 10) >> 10;
被提升為 integer 類型int
。 由於值為正,因此提升的值也是正的。
因此這個操作
c << 10
不接觸符號位。
另一方面,這段代碼片段
signed int a = 0b00000000001111111111111111111111;
signed int b = (a << 10) >> 10;
具有未定義的行為,因為根據 C 標准的同一部分
4 E1 << E2的結果是E1左移E2位; 空出的位用零填充。 如果 E1 具有無符號類型,則結果的值為 E1 × 2E2,比結果類型中可表示的最大值多模一減少。 如果 E1 具有帶符號類型和非負值,並且 E1 × 2E2 在結果類型中是可表示的,那么這就是結果值; 否則,行為未定義。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.