天天看點

c語言中指數形式怎麼表示_C語言中signed char類型,能表示-128到127,為什麼負數多一位?...問題初步分析補碼為什麼 signed char 型能夠表示的數值範圍為 -128~127延伸:為什麼補碼能這麼巧妙實作了正負數的加減運算?小結

初學者在學習C語言,談到不同資料類型時,一般都能了解 unsigned 和 signed 的差別,無非就是有無符号而已。但是對于 signed 資料類型的資料範圍,初學者卻常常會感到迷惑。

c語言中指數形式怎麼表示_C語言中signed char類型,能表示-128到127,為什麼負數多一位?...問題初步分析補碼為什麼 signed char 型能夠表示的數值範圍為 -128~127延伸:為什麼補碼能這麼巧妙實作了正負數的加減運算?小結

對于 signed 資料類型的資料範圍,初學者卻常常會感到迷惑

問題

例如提到 char 型能夠表示的資料範圍時,unsigned char 類型很好了解,所有的 8 位全部用于表示數值,是以 unsigned char 類型能夠表示 2^8 = 256 個數,例如 0 到 255。

對于 signed char 類型,最高一位表示符号(+/-),能夠用于表示數值的是低 7 位。是以按理說,signed char 類型能夠表示的數值範圍為 -0b111111到 +0b1111111,也即 -127 到 127。

但是很多教科書上卻說 signed char 類型能夠表示的數值範圍為 -128 到 127,類似的還有 signed short 類型能夠表示的數值範圍為 -32768 到 32767,signed int 類型能夠表示的數值範圍為 -2147483648 到 2147483647,為什麼有符号的整數類型,負數部分總是比整數部分多出一個數呢?

這裡假設 short 類型占用 2 位元組記憶體空間,int 類型占用 4 位元組記憶體空間。
c語言中指數形式怎麼表示_C語言中signed char類型,能表示-128到127,為什麼負數多一位?...問題初步分析補碼為什麼 signed char 型能夠表示的數值範圍為 -128~127延伸:為什麼補碼能這麼巧妙實作了正負數的加減運算?小結

為什麼有符号的整數類型,負數部分總是比整數部分多出一個數呢?

初步分析

在C語言中,char 型變量占用 8 個位,對于 signed char 類型,最高位表示符号位,此時有 7 個位用于表示數值。按照數學中的排列組合,7 個位能夠表示 2^7 也即 128 個不同的數,若考慮正負号,signed char 類型最多也能表示 2*128=256 個不同的數。

但是,如果 signed char 類型能夠表示的數值範圍是 -0b111111到 +0b1111111(-127 到 127),那麼能夠表示的隻有 255 個不同的數字了,與理論最大能夠表示的不同數字數 256 相比,少了一個,這是因為 -0 和 +0 其實是同一個數字,也即 0b10000000 和 0b00000000 是同一個數字 0。

這對于計算機來說很不友好,同樣的一個數字有兩種二進制碼,在處理時會顯得很麻煩。這種麻煩對于 CPU 的電路設計,同樣如此。

談到 CPU 的電路設計,我們還應該明白,固定的面積上能夠容納的電路單元數是固定的。如果 CPU 隻需處理加法運算,那麼設計師就隻需設計加法電路,這樣既簡單,又能最大程度的保障計算力。

c語言中指數形式怎麼表示_C語言中signed char類型,能表示-128到127,為什麼負數多一位?...問題初步分析補碼為什麼 signed char 型能夠表示的數值範圍為 -128~127延伸:為什麼補碼能這麼巧妙實作了正負數的加減運算?小結

簡單就好

可是在實際應用中,不可避免的需要減法運算,再考慮到 +/- 0 導緻的數字二進制碼重複問題,計算機中“補碼”的概念被提出了。

補碼

關于“補碼”的定義就不寫了,感興趣的讀者可以自行百科。這裡僅簡要的說下C語言中補碼是如何獲得的,其實很簡單:正數和 0 的補碼等于自身,負數的補碼則是将其對應正數按位取反再加1。

例如正數 0b00000011(3) 的補碼等于其自身,仍然為 0b00000011,而 0b10000001(-1)的補碼等于 0b11111110 + 1 也即 0b11111111(0xFF)。

補碼的最大優點是可以在加法或減法進行中,不需因為數字的正負而使用不同的計算方式。隻要一種加法電路就可以處理各種有符号數加法(減法可以用一個數加上另一個數的補碼來表示),是以隻要有加法電路及補碼電路即可完成各種有符号數加法及減法,在電路設計上相當友善。

c語言中指數形式怎麼表示_C語言中signed char類型,能表示-128到127,為什麼負數多一位?...問題初步分析補碼為什麼 signed char 型能夠表示的數值範圍為 -128~127延伸:為什麼補碼能這麼巧妙實作了正負數的加減運算?小結

判斷數字是否為 0 時,隻要比較一次即可

另外,補碼下的 0 就隻有一個表示方式,是以在判斷數字是否為 0 時,隻要比較一次即可。

簡單來說,數字 a(正負數皆可)的補碼即為 -a。

為什麼 signed char 型能夠表示的數值範圍為 -128~127

因為 -0 和 +0 其實是同一個數字,是以原碼中 0b10000000 和 0b00000000 都表示數字 0。現在補碼下的 0 隻有一個表示方式:0b00000000,二進制碼 0b10000000 就多餘出來了。

浪費是可恥的,多出的二進制碼 0b10000000 不能白白丢棄。若考慮數字 0 的二進制碼 0b00000000,從它的符号位來看,計算機應該是将其當做“正數”的,0~127 是 128 個“正數”。

現在考察多出的二進制碼 0b10000000,從它的符号位來看,把它當做負數是合情合理的,事實上在C語言中,它表示-128,從 -128 到 -1,恰好是 128 個“負數”。

在C語言中,signed char 型二進制碼 0b10000000 的補碼仍然為 0b10000000,是以它是“數字a的補碼為 -a”原則的例外。

下表是一些 char 類型整數的補碼,可表示的範圍為 -128 到 127,總共 256 個不同整數。

c語言中指數形式怎麼表示_C語言中signed char類型,能表示-128到127,為什麼負數多一位?...問題初步分析補碼為什麼 signed char 型能夠表示的數值範圍為 -128~127延伸:為什麼補碼能這麼巧妙實作了正負數的加減運算?小結

char 類型整數的補碼,可表示的範圍為 -128 到 127

延伸:為什麼補碼能這麼巧妙實作了正負數的加減運算?

答案是:指定 n 位字長,就有 2^n 個可能的值,加減法運算都存在上溢出與下溢出的情況,實際上都等價于模 2^n 的加減法運算。這對于 n 位無符号整數類型或是 n 位有符号整數類型都同樣适用。

例如,8 位無符号整數的值的範圍是 0 到 255。是以 4+254 将上溢出,結果是 2,即

4+254 = 258 = 2 (mod 256)
           

8 位有符号整數的值的範圍,如果規定為−128到127,則 126+125 将上溢出,結果是−5,即

126+125 = 251 = -5(mod 256)
           
c語言中指數形式怎麼表示_C語言中signed char類型,能表示-128到127,為什麼負數多一位?...問題初步分析補碼為什麼 signed char 型能夠表示的數值範圍為 -128~127延伸:為什麼補碼能這麼巧妙實作了正負數的加減運算?小結

為什麼補碼能這麼巧妙實作了正負數的加減運算?

對于 8 位字長的有符号整數類型,以 2^8 即 256 為模,則

-128 = 128 (mod 256)-127 = 129 (mod 256)...-2 = 254 (mod 256)-1 = 255 (mod 256)
           

是以模 256 下的加減法,用 0, 1, 2,…, 254,255 表示其值,或者用 −128, −127,…, −1, 0, 1, 2,…,127 是完全等價的。−128與128,−127與129,…,−2與254,−1與255 可以互換而加減法的結果不變,需要的 CPU 加法運算器的電路實作與 8 位無符号整數并無不同。

實際上對于 8 位的存儲單元,把它的取值 [00000000,…, 11111111] 解釋為 [0, 255],或者 [-1, 254],或者[-2, 253],或者[-128, 127],或者[-200, 55],甚至[500, 755],對于加法硬體實作并無不同,都是一樣的。

小結

本節主要讨論了C語言中 signed char 型變量能夠表示的數值範圍,一般認為其能夠表示 -128~127 的整數,而不是 0b11111111(-127)到 0b01111111(127),這也是初學者常常感到迷惑的地方。其實簡單來看,計算機的設計讨厭一切“浪費”,數字 0 用兩個二進制碼(+/-0)表示就是一種浪費,處理起來也比較麻煩。倒不如隻為 0 保留一種二進制碼 0b00000000,而将多出的二進制碼 0b10000000 用于表示 -128 友善了。

c語言中指數形式怎麼表示_C語言中signed char類型,能表示-128到127,為什麼負數多一位?...問題初步分析補碼為什麼 signed char 型能夠表示的數值範圍為 -128~127延伸:為什麼補碼能這麼巧妙實作了正負數的加減運算?小結

點個贊再走吧

歡迎在評論區一起讨論,質疑。文章都是手打原創,每天最淺顯的介紹C語言、linux等嵌入式開發,喜歡我的文章就關注一波吧,可以看到最新更新和之前的文章哦。

未經許可,禁止轉載。