深刻理解計算機系統(2.6)------整數的運算

  前面兩篇博客咱們詳細講解了計算機中整數的表示,包括有符號和無符號(補碼編碼)的詳細介紹。那麼這篇博客咱們將對它們的運算有個詳細的瞭解。java

  在講解以前首先看下面的一個程序,看看輸出結果是啥?優化

#include <stdio.h>

int main()
{
	int i = 2147483647;
    printf("%d\n",i+1);
    printf("%d\n",i+i);
	return 0;
}

  結果是:編碼

  

  咱們預期的:spa

  i+1 = 2147483647 + 1 = 2147483648blog

  i+i = 2147483647 + 2147483647  = 4 294 967 294圖片

  爲何程序中的結果和咱們數學中的常識會有這麼大的區別?兩個正數相加獲得負數。這就須要咱們理解計算機中整數的運算原理。編譯器

 

一、計算機整數運算的侷限

  咱們知道計算機是用二進制序列來表示數的。而二進制序列的長度是和計算機自己的字長有關。不一樣的數據類型定義的二進制序列長度不同,即不一樣的數據類型表示數的大小範圍是不同的。可是不論是什麼數據類型,它定義的二進制序列長度是有限的,即它表示的數的大小範圍是有限的博客

  因此兩個數作運算,若是結果超出了定義數據類型所表示數的大小範圍,那麼結果將會出現失真。並且這個失真的結果也不是隨機的,而是有跡可循的,那麼究竟是怎麼產生失真的,請接着往下面看。數學

  PS:下面給出 64 位機器上C語言的整型數據類型的取值範圍。本篇博客中程序運行環境都是在64位系統中進行。io

  

 

二、無符號數加法運算

  前面咱們講過,對於一個 w 位的無符號二進制整數[xw-1 , xw-2 , … , x2 , x1 , x0],其值大小知足 0 <= x <= 2w-1.

  若是兩個無符號數相加,那麼其結果應該是 0 <= x+y <=2w+1-2。很顯然表示這個範圍的數必需要 w+1 位二進制。

  當咱們對無符號數作加法運算的時候,若是結果超過了 2w-1,那麼這個結果就會失真。

#include <stdio.h>

int main()
{
	unsigned short int i = 65535;
	unsigned short int j = i+1;
    printf("%u\n",j);
	return 0;
}

  結果爲:

  

  對於上面的程序,咱們是在64位系統中進行運算。由上面給出的圖片咱們能夠知道 unsigned short int 在計算機中佔用 2 個字節。表示的數據範圍是 0——216-1,即0——65535

  咱們在程序中定義 i = 65535,那麼 i+1=65536,這個結果是超出了 unsigned short 表示的數據範圍。因此結果失真了,可是結果爲何是 0 呢?

  上一篇博客咱們講過C語言中二進制數的截斷:

  將一個 w 位的數 [xw-1 , xw-2 , … , x2 , x1 , x0] 截斷爲一個 k 位數字時,咱們會丟棄高 w-k 位。獲得 [xk-1 , xk-2 , … , x2 , x1 , x0]

  對於上面的 i = 65535,二進制表示爲:[1111 1111 1111 1111],加1 結果爲 65536,用二進制表示爲 [1 0000 0000 0000 0000],爲了將結果保持在 4個字節,即32位二進制序列,咱們去掉最高位的 1,那麼結果就變成了 [0000 0000 0000 0000],也就是打印出來的結果 0.

  通常而言,無符號加法等價於計算和模上2w

  好比上面的二者計算和爲 65536,模上 2w,即模上216=65536,結果爲0

  ps:模表示二者相處取餘

  如今定義 0<= x,y <2w,那麼它們運算知足下面關係:

  

  注意:當 2<= x+y < 2w+1,對 x + y 進行2w的取模運算,與 x + y - 2w是等價的。

  因此若是兩個無符號整數做加法運算。當 x+y < 2w 時,它們的結果不變;當 2<= x+y < 2w+1,它們的結果爲 x+y-2w

  

三、補碼加法運算 

  對於補碼加法運算,由於補碼編碼是表示有符號的整數。

  對於一個 w 位的補碼二進制整數[xw-1 , xw-2 , … , x2 , x1 , x0],其值大小知足 -2w-1  <= x <= 2w-1-1。那麼 -2w <= x+y <=2w-1

  想要表示上面的兩個數相加和的範圍,那麼可能須要 w+1 來表示。這裏咱們也須要截取。

  與無符號加法運算不一樣,補碼加法會出現三種狀況:正溢出、正常、負溢出。定義以下:

    範圍在  -2w-1  <= x,y <= 2w-1-1 作加法運算時,知足

  

  簡單來講:補碼加法運算就是先按照無符號加法進行運算,然後在進行無符號和有符號的轉換。

   

這裏咱們看個例子:

#include <stdio.h>

int main()
{
	short int i = -32768;
 	short int j = i-1;
    printf("%d\n",j);
	return 0;
}

  結果爲:

  

  爲何 -32768-1 結果會是 32767?

  根據上面的公式:

  

  咱們須要先將 -32768 和 -1 分別轉換成無符號數進行加法運算,而後對獲得的結果轉換成有符號數。

  ①、-32768 轉換成無符號數也就是 -32768+2^16=32768  

  ②、-1 轉換成無符號數也就是-1+2^16=65535 

  ③、將上面兩步的結果相加,而後轉換成有符號數:

    即(65535+32768)-2^16=65535+32768-65536=32767

  這個過程用到的公式分別有:

  

四、無符號數乘法運算

  對於一個 w 位的無符號二進制整數[xw-1 , xw-2 , … , x2 , x1 , x0],其值大小知足 0 <= x <= 2w-1.

  若是兩個無符號數相乘,那麼其結果應該是 0 <= x*y <=(2w-1)2=22w-2w+1+1。很顯然表示這個範圍的數可能須要 2w 位來表示。也就是 2w 位的整數乘積的低 w 位表示的值。根據咱們前面講的截斷原理:能夠看作是計算乘積模2w,即:

  

#include <stdio.h>

int main()
{
	unsigned short int i = 2;
 	unsigned short int j = i*2;
    printf("%u\n",j);
	return 0;
}

  好比上面的程序結果是:(2*2)mod 216=4 mod 65536=4

  

五、補碼乘法 

   對於一個 w 位的補碼二進制整數[xw-1 , xw-2 , … , x2 , x1 , x0],其值大小知足 -2w-1  <= x <= 2w-1-1

  那麼它們的乘積x*y的取值範圍在 -2w-1*(2w-1-1)=22w-2+2w-1 到 -2w-1*2w-1=-22w-2 之間。

  同理 2w 位的整數乘積的低 w 位表示的值。根據咱們前面講的截斷原理:補碼乘法運算公式爲

    

 

   

  假設對於w位的兩個補碼數來講,它們的乘積的低w位與無符號數乘積的低w位是同樣的。這意味着計算機可使用一個指令執行無符號和補碼的乘法運算。下面咱們來證實:

  其中x’和y’分別表明x和y的補碼編碼。

  那麼:(應用有符號轉爲無符號公式可得)

  

  即:          (2w mod 2w = 0)

 

   因爲模運算符,全部帶權重 2w 的項都丟棄了,所以咱們看到 x*y  和 x’*y’ 的低 w 位是相同的。

 

六、乘法優化

  因爲在大多數機器上,整數乘法指令至關慢,須要 10 個或多個時鐘週期,而其餘整數運算(好比加法、減法、位級運算和移位)只須要 1 個時鐘週期。

  所以編譯器使用了一項重要的優化,使用移位和加法的組合來代替乘法。

  結論:對於一個w位的二進制數來講,它與2k的乘積,等同於這個二進制數左移k位,在低位補k個0。

  證實過程以下:

   

  咱們前面說過,整數乘法代價要比移位和加法代價大得多。那麼C編譯器會以移位、加法、減法的組合來消除不少整數乘以常數的狀況。

  好比:

    計算 x*14 的乘積。 因爲 14 = 23+22+21   ,那麼編譯器會將乘法重寫爲(x<<3)+(x<<2)+(x<<1)。這樣就將乘法替換爲三個移位和兩個加法。不管 x 是無符號仍是補碼,甚至當乘法會致使溢出時,兩個計算都會獲得同樣的結果。

    更好的編譯器,可能會將 14 = 24-21。那麼就會變成(x<<4)-(x<<1),只須要兩個移位和一個減法。

 

七、除法運算

  實際上在大多數機器上,整數除法要比整數乘法更慢,須要 30 或更多個時鐘週期。

  結論:對於除以 2 的冪能夠用移位來運算。無符號除法使用邏輯移位,補碼除法使用算術移位。

  ①、邏輯右移在左端補k 個0。C語言中對於無符號數據必須邏輯右移。

  對於位向量[xw-1,xw-2,...,x0]邏輯右移 k 位會獲得位向量:[0,...,0,xw-1,xw-2,...,xk]。轉換成除法即 x/2k,從結果咱們能夠看出邏輯移位出現小數,老是舍入到零,好比 7/2應該是 3,而不是4

    

 

  ②、算術右移是在左端補 k 個最高有效位的值。對於一個正整數,因爲最高有效位是 0 ,因此效果和邏輯右移是同樣的;對於非負數,算術右移 k 位與除以 2k 是同樣的。

    對於結果不須要舍入的狀況結果是正確的。可是對於結果須要舍入的時候,算術右移致使的結果是向下舍入,好比 -7/2應該是 -3,而不是 -4。這是錯誤的。

    

 

八、總結

  那麼本篇博客結束咱們對於整數的表示以及運算都已經瞭解了。注意整數的運算我沒有將減法,其實減法也就是轉換爲補碼相加。並且計算機中也只有加法器,是沒有減法器的。咱們只須要將減法轉換爲加法運算便可。

  整數的表示和運算結束了,下一篇博客咱們將會講解浮點數,也就是有小數的數。

相關文章
相關標籤/搜索