細說計算機底層整型編碼機制

現在計算機的抽象級別愈來愈高,愈加少人關注在計算機底層發生了什麼事情,其實底層也有些頗有意思的東西。這篇文章主要想科普一下整型在計算機硬件中的相關實現,它是以什麼方式來存儲的?如何區別正負數?硬件會怎麼去解釋相關的位串?編程

無符號數

在現代編程語言中,支持無符號數的語言已經比較少了。常見的就只有C/C++,它們能夠經過unsigned關鍵字定義無符號相關的數據類型。無符號整型與有符號整型最大的區別就是對相同的二進制位串解讀方式不同。全部的無符號數都是非負數,由於它是沒有符號的。若是咱們要用位長爲w的空間來存儲整型,以無符號的方式來解讀的話第k位的權重都是2 ^ k,其中最高有效位的權重爲2 ^ (w - 1)bash

舉個具體點的例子,假設二進制串11000001所表明的是一個無符號數,因爲它是8位長,因此最高有效位的權重是2 ^ (8 - 1) = 128,第0位的權重是2 ^ 0 = 1, 第一位的權重是2 ^ 1 = 2,以此類推。最終代入公式可得編程語言

2 ^ 7 * 1 + 2 ^ 6 * 1 + 2 ^ 5 * 0 + 2 ^ 4 * 0 + 2 ^ 3 * 0 + 2 ^ 2 * 0 + 2 ^ 1 * 0 + 2 ^ 0 * 1 = 193
複製代碼

所以,二進制串11000001所對應的無符號數爲193。還能夠猜想出在8位的空間內最小的無符號數是0,它的二進制表示爲00000000,最大的無符號數是255,它的二進制表示爲11111111編碼

雖然說,在平常的業務邏輯中用到無符號數的機會已經很少了,不過在計算機底層你仍是能夠常常看到它的身影。假設咱們要用如下的16位的二進制串來表明整型spa

11100000 10000000
複製代碼

其中高8位爲11100000,低8位爲10000000。無論這個16位的位串最終會被解讀成有符號數仍是無符號數,低8位始終均可以看做一個無符號數128。同理,若是是一個表明整型的32位二進制串code

11100000 10000000 11100000 10000000
複製代碼

那麼它的低24位就能夠看做一個無符號數了。從這個角度來看,雖然說咱們平時不會直接操做無符號數,但實際上無符號數在計算機底層仍是「大量存在」的。數學

有符號數-原碼,反碼,補碼

若是在整型的世界中只有無符號數的話,那麼彷佛難以構建一個龐大的系統,畢竟有許多數據或者運算都要藉助負數。接下來我想簡單介紹一下在計算機底層要表示一個有符號的整型主要有哪些策略。it

1. 原碼(Sign-Magnitude)

這種編碼方式比較好理解,前面所說的無符號數之因此沒法表示負數,那是由於咱們並無留下一個特殊的位置用於標識它究竟是大於0的仍是小於0的。而原碼的基本原理其實就是分配一個位用來標識該數究竟是正數仍是負數class

一樣用8位空間來舉例子,採用最高有效位來做爲符號位,其餘位用於權重計算,那麼不難看出它所能表示的最大的數就是01111111,最小的數就是11111111。它們的值分別爲127-127,不過現在的大多數機器都不是以這種方式來表示有符號整型的。並且這種方式還有個比較突出的特性,若是要表示數字0,將有兩種二進制編碼方式,+0會表示爲00000000,-0會表示爲10000000。也許這種不惟一性也是它沒有在整型的領域被普遍採用的緣由吧。不過原碼這種編碼方式在浮點數裏面會用到,這個之後有機會再說。原理

2. 反碼(Ones' complement)

記得是小學數學裏面就已經出現了減法運算了,可是我是到了初中才接觸到負數的概念。一個負數其實就是某個特定正數的相反數。正數56的取反結果就是-56,那麼在二進制裏面怎麼取反呢?一個可行的辦法就是按位取反,這也是反碼的最直觀的特徵。00111000所表明的數值是56,若是要用反碼來表示-56直接把56的二進制串按位取反便可11000111

不過上面所說的只是反碼給人最直觀的感受,實際上它仍是須要必定的數學支持的。在反碼裏面最高有效位的權重爲- (2 ^ (w - 1) - 1),若是是一個8位的位串,最高有效位的權重爲-127,其餘位的計算方式跟無符號數同樣。那麼把前面所得的11000111二進制串代入公式可得

-(2 ^ 7 - 1) * 1 + 2 ^ 6 * 1 + 2 ^ 5 * 0 + 2 ^ 4 * 0 + 2 ^ 3 * 0 + 2 ^ 2 * 1 + 2 ^ 1 * 1 + 2 ^ 0 * 1 = -56
複製代碼

結果恰好是-56。這個時候,不難推斷出一個字節所能表示的最大值爲127(01111111),最小值爲-127(10000000)。反碼所可以表示的數值範圍跟原碼同樣,只是某些數值在底層的編碼方式會有所不一樣。此外,它跟原碼都有一樣的問題,就是對數字0有兩種不一樣的二進制表示方式。在反碼中+0表示爲00000000,-0表示爲11111111

3. 補碼(Two's complement)

理論上,原碼和反碼都可以用來表示有符號數,不過他們都有奇怪的屬性,就是對數字0分別會有兩種不一樣的位模式。而這裏要說的補碼就能很好地解決這種問題。

與原碼,反碼相似,補碼也是要靠最高有效位來影響數值的正負。對於長度爲w位的補碼錶示,最高有效位爲w - 1,最高有效位的權重爲2 ^ (w - 1)。不難推斷出在一個字節長的空間內,補碼所可以表示的有符號數最大值爲127(011111111),最小值爲-128(11111111)。

若是要用補碼來表示整型56,它所對應的二進制串依然是00111000。若是要表示-56則有

- 2 ^ 7 * 1 + 2 ^ 6 * 1 + 2 ^ 5 * 0 + 2 ^ 4 * 0 + 2 ^ 3 * 1 + 2 ^ 2 * 0 + 2 ^ 1 * 0 + 2 ^ 0 * 0 = -56
複製代碼

所以,-56的補碼錶示爲11001000。在補碼下-5656兩個數值對應的位模式之間有如下關係

11001000 + 00111000 = 1 00000000
複製代碼

它們相加剛好等於1 00000000,然而在底層咱們只用了一個字節的空間來存儲它們相加的結果,所以須要對最終結果進行截斷處理。最終獲得的結果是00000000。是否是有點意思?截斷以後剛好就是56 + (-56) = 0,這也是補碼優雅之處。

與原碼,反碼相比,補碼較大的好處就是映射具備惟一性,對於數值0不會再有兩種不一樣的表示方式了。在位長爲w的空間中,補碼可以多表示一個數值- 2 ^ (w - 1)。此外,它能表示的數值剛好一半是非負數0 ~ 2 ^ (w - 1) - 1,另外一半是負數- (2 ^ (w - 1)) ~ -1。對8位二進制串來講則是非負數範圍是0 ~ 127,負數範圍是-128 ~ -1

無符號與補碼的關係

在相同的位模式下,無符號表示與補碼錶示最大的區別就是最高有效位的權重不一樣。位長爲w的無符號表示中,最高有效位的權重爲2 ^ (w - 1),而補碼錶示的最高有效位權重爲-2 ^ (w - 1)。當最高有效位爲0時,它們所表示的數值相同。只有在最高有效位爲1的時候,二者之間所表示的數值纔會有所不一樣,這個時候它們所表示的數值之間相差|2 ^ (w - 1) - (- 2 ^ (w - 1))| = 2 ^ w

簡單舉兩個例子

a. 最高位爲0

以無符號的方式來解讀二進制位模式00011110,代入公式可得

2 ^ 7 * 0 + 2 ^ 6 * 0 + 2 + 2 ^ 5 * 0 + 2 ^ 4 * 1 + 2 ^ 3 * 1 + 2 ^ 2 * 1 + 2 ^ 1 * 1 + 2 ^ 0 * 0 = 30
複製代碼

以補碼的方式來解讀可得

-2 ^ 7 * 0 + 2 ^ 6 * 0 + 2 + 2 ^ 5 * 0 + 2 ^ 4 * 1 + 2 ^ 3 * 1 + 2 ^ 2 * 1 + 2 ^ 1 * 1 + 2 ^ 0 * 0 = 30
複製代碼

此時最高有效位爲0,所以它的權重對大局來講沒有任何影響。二進制位模式00011110不管是以無符號的方式去解讀仍是以補碼,原碼,反碼的方式去解讀,它所表明的數值都是30

b. 最高位爲1

當最高有效位爲1的時候二者之間差異就比較大了。這個時候補碼所表示的老是負數。

對二進制位模式10001001,以無符號的形式進行轉換,代入公式可得

2 ^ 7 * 1 + 2 ^ 6 * 0 + 2 ^ 5 * 0 + 2 ^ 4 * 0 + 2 ^ 3 * 1 + 2 ^ 2 * 0 + 2 ^ 1 * 0 + 2 ^ 0 * 1 = 137
複製代碼

而以補碼的方式去解讀則會有

- 2 ^ 7 * 1 + 2 ^ 6 * 0 + 2 ^ 5 * 0 + 2 ^ 4 * 0 + 2 ^ 3 * 1 + 2 ^ 2 * 0 + 2 ^ 1 * 0 + 2 ^ 0 * 1 = -119
複製代碼

兩種換算方式所獲得的數值相差甚遠,前面推導過二者相差2 ^ w。這個例子中就是137 - (-119) = 256,剛好是2 ^ 8

總結

這篇文章主要簡單介紹一下在要用二進制來表示整數有哪幾種不一樣的編碼策略,雖然說大多數狀況下咱們都不須要關心底層到底發生了什麼(除非你是在用C/C++來編寫代碼),不過看成簡單的科普知識來了解一下仍是挺有趣的。

相關文章
相關標籤/搜索