簡體   English   中英

轉換有符號的 int 與 short 時符號擴展的不一致

[英]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 節中有詳細說明:

可以在可以使用intunsigned int的表達式中使用以下內容

  • object 或具有 integer 類型(除intunsigned int之外)的表達式,其 integer 轉換等級小於或等於intunsigned int等級。
  • _Boolintsigned intunsigned 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 shortf仍被提升為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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM