1. 程式人生 > >計算機編碼--為什麽整數中負數的除法和右移不是一回事

計算機編碼--為什麽整數中負數的除法和右移不是一回事

什麽 lock 其中 img csdn leg tail 計算 的區別

緣起

  最近在看卡耐基梅隆大學的【深入理解計算機系統實驗】之datalab時,遇到一個題目:

 1 /* 
 2  * divpwr2 - Compute x/(2^n), for 0 <= n <= 30 
 3  *  Round toward zero 
 4  *   Examples: divpwr2(15,1) = 7, divpwr2(-33,4) = -2 
 5  *   Legal ops: ! ~ & ^ | + << >> 
 6  *   Max ops: 15 
 7  *   Rating: 2 
 8  */  
 9 int
divpwr2(int x, int n) { 10 11 }

即只能用題目提供的操作實現 x/(2^n) 的計算。對於正數,沒什麽可說的,直接x>>n即可。

但是負數也是這樣嗎,仔細一看,才發現 x/(2^n) 和 x>>n不是一回事。

比如 -33/(2^4) = -2, 但是另一方面,-33的編碼為0xFFFFFFDF,右移4位變成0xFFFFFFFD,即-3,顯然不一致。

思考中的犯傻

  對於上述問題,在做最後的解釋前,先插入一段我的思考過程,主要是記錄其中的犯傻之處。

由於計算機中整數的表示法為 2的補碼(two‘s complemet representation)

,如下公式:

技術分享圖片

其中s為符號位,x為其他位,N為位數。

對於正數,s為0,只剩後面的求和項,除以(2^n)的話,從公式上看確實是直接右移n位即可。

接著這個思路,我就想,那麽負數除以(2^n)等於:

技術分享圖片

其中第二項跟正數一樣,右移n位即可,第一項是什麽呢?c語言中的int類型位數太長,這裏簡單以8位代替。

-2^(8-1) = -128(十進制) = 1000 0000(二進制)

-2^(8-1-1)= -64(十進制) = 1100 0000(二進制)

-2^(8-1-2)= -32(十進制) = 1110 0000(二進制)

也就是說,前面說的第一項其實也是原式的第一項右移n位。所以,總體來說負數除法也是右移。

但是,這個結論顯然與-33/(2^4)那個例子矛盾,可是錯誤在哪兒呢?

其實,錯誤就在於把x掰開成兩個,然後整除2^n,在把結果加起來,這個過程與x直接整除2^n是不等價的。

比如:6/2=3,但是 3/2+3/2=1+1=2。雖然錯誤很明顯,但是一開始思考的時候卻犯傻了。技術分享圖片

原因應該是什麽

負數除法與移位不同的原因用下面一張圖就能說明白:

技術分享圖片

  如圖所示,中間是一條數軸,數軸上面是x與x/(2^4)的對應關系,下面是x與x>>4的對應關系,

設 x/(2^4)=f(x)>>4,那麽從上圖可以看出,當x/(2^4)得到-1時,f(x)>>4為了得到-1,f(x)要比x向右移動15,或2^4-1。

圖中具體是-16->-1或者-31->-16。

  因此,x / (2^n) = (x + (2^n-1)) >> n,所以,datalab那個題目的答案可以是:

1 int divpwr2(int x, int n) {  
2     //all zeros or all ones  
3     int signx=x>>31;  
4     //int mask=(1<<n)+(-1);  
5     int mask=(1<<n)+(~0);  
6     int bias=signx&mask;  
7     return (x+bias)>>n;  
8 } 

參考文章:

(1)負數的除法和右移的區別;

(2)datalab 深入理解計算機系統實驗。

計算機編碼--為什麽整數中負數的除法和右移不是一回事