簡體   English   中英

C中具有無符號變量的算術

[英]Arithmetic with unsigned variables in C

例如,未簽名的char如何從-128+127取值? 據我了解,最高有效位用於表示數字的符號,而char的其余位用於表示數字的大小。 現在,7位的最大可能大小為127 ,所以范圍不應該在-127+127嗎? -128如何成為結果?

其次,以下行為背后的位邏輯是什么

#include <stdio.h>

int main()
{
    signed char x = 127;
    x += 1;
    printf("%i", x);
}

輸出:

-128

可以看到, x變為-128 ,但是為什么呢? 這種行為背后的算術是什么?

該方法基於名為Two's Complement的東西。 這里的想法是,給定一些二進制數,它是兩個補碼,就是一個補碼(翻轉所有位)加一個。 我們可以看到一個簡單的示例,讓我們找到13的二進制補碼,我們可以將其寫為0b01101 01101 (flip) -> 10010 (+1) --> 10011

現在,盡管如果我們像往常一樣將其解釋為二進制數,則將十進制讀為19 ,但我們必須知道該數字寫在Two's Complement中,以便顛倒該過程並得出先前的數字13 因此,從中我們可以看到我們已經表示了+13 = 01101-13 = 10011 ,請注意,正數以0開頭,並且以1對稱。 這將使用此表示時是一個常數,正數將始終以開始0 ,並用消極的1 值得注意的是,我在原始表示形式13前面加上了0 ,這是正確表示其補碼的必要條件。 您可以嘗試通過相同的示例,而無需這樣做並驗證它的必要性。

現在,讓我們看一下這樣表示的一些值,

╔══════╦════════════════╦════════════════════════╗
║ Bits ║ Unsigned Value ║ Two's Complement Value ║
╠══════╬════════════════╬════════════════════════╣
║ 011  ║ 3              ║ 3                      ║
╠══════╬════════════════╬════════════════════════╣
║ 010  ║ 2              ║ 2                      ║
╠══════╬════════════════╬════════════════════════╣
║ 001  ║ 1              ║ 1                      ║
╠══════╬════════════════╬════════════════════════╣
║ 000  ║ 0              ║ 0                      ║
╠══════╬════════════════╬════════════════════════╣
║ 111  ║ 7              ║ -1                     ║
╠══════╬════════════════╬════════════════════════╣
║ 110  ║ 6              ║ -2                     ║
╠══════╬════════════════╬════════════════════════╣
║ 101  ║ 5              ║ -3                     ║
╠══════╬════════════════╬════════════════════════╣
║ 100  ║ 4              ║ -4                     ║
╚══════╩════════════════╩════════════════════════╝

如您所見,它的工作原理與我們之前預期的一樣,但是現在您可以開始了解發現的“錯誤”是如何發生的。 二進制補碼中4位表示的上限是十進制值3 讓我們看一下如何通過簡單地加1來達到-4 3 = 0b011因此3+1 = 0b100 ,如您從表中看到的3+1 = 0b100 ,它映射到Two's Complement上的-4 (而不是4 )。 您的情況就是這個確切的問題,但是有更多的問題。 這樣的帶符號表示是圓形的,因此頂部溢出會產生底部值。 看你的情況

127 = 0b01111111
127 + 1 = 0b10000000

如您所見,它以1開頭,因此它是負數(!),並且如果您解決了Two's Complement,您將看到它表示-128(因為下限始終大於上限)。

假定並非每種硬件都將以相同的方式實現事物,英特爾,AMD,ARM,據我所知,所有通用CPU的主要體系結構在其ALU中都使用二進制補碼,但是有硬件將其他技術用於實現整數的符號,因此從根本上說,您描述的行為是不確定的。 要注意的另一件有趣的事是, IEEE的浮點算法標准實現了基於指數偏差的有符號浮點。

最后,由於我們在這里討論的是C,因此請務必注意,未定義的行為可以由編譯器優化,因此在本博文中描述了此類優化的一個很好的例子。

在C語言中,通過=+運算符的等效組合定義了+=運算符的行為。 例如,按照定義,您的x += 1代表x = x + 1 由於x具有窄型帶signed char ,因此在任何算術運算開始之前它都會被提升為int 這意味着子表達式x + 1int類型的域中求值。 之后,結果(類型為int )將轉換回帶signed char並存儲回x

因此,在您的情況下,您的x += 1實際上等於

x = (signed char) ((int) x + 1);

(int) x + 1子表達式不會溢出。 它成功產生int類型的值128 但是,此值不適合帶signed char類型的范圍,當將此值轉換回帶signed char類型時,會導致實現定義的行為。 在你這個平台實現定義的行為都產生值-128signed char

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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