CSAPP DATA LAB1————位運算

默默默小西發表於2020-04-07

1題目與解答:

./* 
 * bitAnd - x&y using only ~ and | 
 *   Example: bitAnd(6, 5) = 4
 *   Legal ops: ~ |
 *   Max ops: 8
 *   Rating: 1   
 
 */
int bitAnd(int x, int y) {
  return ~(~x|~y);
}


題意解析:
這就是實現與的功能,由邏輯概念A&B=!(!A|!B) 很容易解決

2.題目與解答
/* 
 * getByte - Extract byte n from word x
 *   Bytes numbered from 0 (LSB) to 3 (MSB)
 *   Examples: getByte(0x12345678,1) = 0x56
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 6
 *   Rating: 2  (x>>8*n)&0xff
 */
int getByte(int x, int n) {
 
  return ((x>>(n<<3))&0xff);
 
}
題意解析:
這題的意思就是取位值
對於0x12345678
當n=0時,return 0x78
當n=1時,return 0x56
當n=2時,return 0x34
當n=3時,return 0x12
解答過程與思考:
首先,在這個過程中,x應先右移8*n位再取最低的8位數值
即 return (x>>8*n)&0xff
但是 不能用*  所以採用移位
return ((x>>(n<<3))&0xff);

3.題目與解答
/* 
 * logicalShift - shift x to the right by n, using a logical shift
 *   Can assume that 0 <= n <= 31
 *   Examples: logicalShift(0x87654321,4) = 0x08765432
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 20
 *   Rating: 3 
 */
int logicalShift(int x, int n) {
  int mask=~(((1<<31)>>n)<<1);
  return (x>>n)&mask;
}


題意解析:
這題的意思就是邏輯右移位(預設為算術右移位)
解答過程與思考:
首先,考慮0x0000 0000 =>return x>>n
再考慮正數,0x0xxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx
也只需要return x>>n
再考慮負數 1xxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx
取(0x-87654321,4)
1000 7 6 5 4 3 2 1 右移4位=》1111 1000 7 6 5 4 3 2 
發現符號位對結果產生了干擾
所以 新增一個掩碼(掩碼只需要把符號位轉為0)
對於這一個負數的 掩碼應該為0x0FFFFFFF
若n=5 則掩碼為0x07FFFFFFF....
觀察這些掩碼,很容易發現,若右移n位,則前n位應為0
所以
嘗試將函式寫為
int logicalShift(int x, int n) {
  int mask=~(((1<<31)>>(n-1));
  return (x>>n)&mask;
測試,報錯 (不可用-號)(
改正
int logicalShift(int x, int n) {
  int mask=~(((1<<31)>>n)<<1);
  return (x>>n)&mask;
(理由:最後要左移一位,因為首位已經是1了,如果左移n位,會多出現一個1)

4.題目與解答

/*
 * bitCount - returns count of number of 1's in word
 *   Examples: bitCount(5) = 2, bitCount(7) = 3
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 40
 *   Rating: 4
 */
 int bitCount(int x) {  
        int result;  
        //int mask1=(0x55)|(0x55<<8)|(0x55<<16)|(0x55<<24);  
        int tmp_mask1=(0x55)|(0x55<<8);  
        int mask1=(tmp_mask1)|(tmp_mask1<<16);  
        //int mask2=(0x33)|(0x33<<8)|(0x33<<16)|(0x33<<24);  
        int tmp_mask2=(0x33)|(0x33<<8);  
        int mask2=(tmp_mask2)|(tmp_mask2<<16);  
        //int mask3=(0x0f)|(0x0f<<8)|(0x0f<<16)|(0x0f<<24);  
        int tmp_mask3=(0x0f)|(0x0f<<8);  
        int mask3=(tmp_mask3)|(tmp_mask3<<16);  
        int mask4=(0xff)|(0xff<<16);  
        int mask5=(0xff)|(0xff<<8);  
        //add every two bits  
        result=(x&mask1)+((x>>1)&mask1);  
        //add every four bits  
        result=(result&mask2)+((result>>2)&mask2);  
        //add every eight bits  
        //result=(result&mask3)+((result>>4)&mask3);  
        result=(result+(result>>4))&mask3;  
        //add every sixteen bits  
        //result=(result&mask4)+((result>>8)&mask4);  
        result=(result+(result>>8))&mask4;  
        //add every thirty two bits  
        //result=(result&mask5)+((result>>16)&mask5);  
        result=(result+(result>>16))&mask5;  
        return result;  
}  


題意解析:
計算一個數字在計算機儲存時,有多少個1
解答過程與思考:
首先,想到通過一個for迴圈
for(i=0;i<32;i++)
{if ((1<<i)&x))
count++;
}
來計算有多少個1
接下來就是轉化問題(自己實在想不到,然後看了下答案 )
發現答案也只是生成一群掩碼,然後按位與,並用變數儲存,最後返回的過程(二叉檢索)

 
5題目與解答:
/* 
 * bang - Compute !x without using !
 *   Examples: bang(3) = 0, bang(0) = 1
 *   Legal ops: ~ & ^ | + << >>
 *   Max ops: 12
 *   Rating: 4 
 */
int bang(int x) {
   return ~ ((x|(~x+1))>>31)&0x1;    
}


 
題意解析:
就是非運算
解答過程與思考:
在整個過程中,只有當x=0的時候,運算結果才為1,而其它均為0
那麼在這個過程中,需要判斷x是否等於0就可以了。
轉而,又想到是否可以通過x*x來判斷是否等於0(此路不通)
最後,又想到對於負數是可以通過符號位來判定其不等於0,那麼是否可以找到一個通式對x做一定的變換,來保證正數也能通過符號位來判定是否等於0
即 x|~x  這樣的話,x為正數時,也能通過其符號位來驗證
然而,對於這個算式,0也同時滿足這個條件
轉而 修正為 x|(~x+1)
然後 函式寫為
int bang(int x) {
   return ((x|(~x+1))>>31)&0x1;    
}
驗證,錯誤。(忘記取反)
修正函式
int bang(int x) {
   return ~((x|(~x+1))>>31)&0x1;    
}

 
6.題目與解答
/* 
 * tmin - return minimum two's complement integer 
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 4
 *   Rating: 1
 */
int tmin(void) {
  return 1<<31;
}


題意解析:
這題非常簡單,只需要求出32位能表示的最小數即可

 
7.題目與解答
/* 
 * fitsBits - return 1 if x can be represented as an 
 *  n-bit, two's complement integer.
 *   1 <= n <= 32
 *   Examples: fitsBits(5,3) = 0, fitsBits(-4,3) = 1
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 15
 *   Rating: 2
 */
int fitsBits(int x, int n) {
  int isNeg = x >> 31;
  int shift = n + ~0;
  return ((isNeg & !((~x) >> shift)) + (~isNeg & !(x >> shift)));
}


題意解析:
一開始怎麼也看不懂題目,後來諮詢了助教,終於明白題目的意思了。x能否被n位二進位制數完全表示
-4 =100 而5 無法表示 

3位有符號數能表示的範圍是100~011,也就是-4~3,而5不在這個範圍內,即無法表示,結果為0;-4在這個範圍內,那麼結果則為1
解答過程與思考:
首先,如果x>=0的話,
對於3位bits 000 001 010 011   
對於4位bits 0000 0001 0010 0011 0100 0101 0110 0111 
發現關係 return (!(x>>(n-1)))
如果x<0
3位  100 101 110 111
4位bits 1000 1001 1010 1011 1100 1101 1110 1111 
發現這些負數取反正好就是一群正數 那麼 對於負數 就可以用
!((~x)>>(n-1))來判定
所以 最後的函式應是
 
  int isNeg = x >> 31;//正數為0 負數為1
  int shift = n + ~0;
  return ((isNeg & !((~x) >> shift)) + (~isNeg & !(x >> shift)));
 
8:題目與解答
/* 
 * divpwr2 - Compute x/(2^n), for 0 <= n <= 30
 *  Round toward zero
 *   Examples: divpwr2(15,1) = 7, divpwr2(-33,4) = -2
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 15
 *   Rating: 2
 */
int divpwr2(int x, int n) {
  //all zeros or all ones  
    int signx=x>>31;  
    //int mask=(1<<n)+(-1);  
    int mask=(1<<n)+(~0);  
    int bias=signx&mask;  
    return (x+bias)>>n;  
} 


 
題意解析:
題目的意思就是返回 x /(2^n)
解答過程與思考:
首先考慮正數,只需要return x>>n 就滿足
再考慮負數  發現符號位產生干擾,可以排除符號位 再在最後補上
取符號位 signbit=(x>>31)<<31
消去符號位 temp=x<<1 然後邏輯右移n+1位
int divpwr2(int x, int n) {
   int signbit=(x>>31)<<31;
   int temp=(x<<1);
   int mask=~((1<<31)>>n);
   temp=((temp>>(n+1))&mask);//邏輯右移n+1
   return (temp|signbit); 

驗證,報錯
沒有考慮到-0的概念
修正函式
參考網路答案:
int divpwr2(int x, int n) {
  //all zeros or all ones  
    int signx=x>>31;  
    //int mask=(1<<n)+(-1);  
    int mask=(1<<n)+(~0);  
    int bias=signx&mask;  
    return (x+bias)>>n;  

它就是把負數變換成正數來處理。
它先通過signx來判斷正負
而對於負數  只需要通過~(~(x-1)>>n)+1來處理(負數補碼與正數補碼的關係)
對於該算式  =(x-1)>n+1=(x-1+1<<n)>>n  這就是它 最基本的意思
~(~(x-1)>>n)+1=(x-1)>>n+1=(x-1+(1<<n))>>n
0xFF FF FF FF =-1  0x00 00 00 01=1  
其實 可以用
return (~(x>>31)&(x>>n))+((x>>31)&(~((~x+1)>>n)+1));
來計算
 
9:題目與解答
 * negate - return -x 
 *   Example: negate(1) = -1. 
 *   Legal ops: ! ~ & ^ | + << >> 
 *   Max ops: 5 
 *   Rating: 2 
 */  
int negate(int x) {  
  return ~x+1;  
}  


題意解析:
返回相反數
解答過程與思考:
在計算機中,數字以補碼形式儲存。所以,只需要 x=~x+1

10:題目與解答
/* 
 * isPositive - return 1 if x > 0, return 0 otherwise 
 *   Example: isPositive(-1) = 0.
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 8
 *   Rating: 3  xian qiu x<0  x>>31 dang x=0 !x
 */
int isPositive(int x) {
  return !((x>>31)|!x);
}


題意解析:
當x大於0,返回1 其餘返回0
解答過程與思考:
首先,通過其符號位判定return !(x>>31)
發現,0不滿足這個式子
修正 return !((x>>31)|!x)
驗證成功
11:題目與解答
/* 
 * isLessOrEqual - if x <= y  then return 1, else return 0 
 *   Example: isLessOrEqual(4,5) = 1.
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 24
 *   Rating: 3
 */
int isLessOrEqual(int x, int y) {
   int sub=y+(~x+1);
  int signx = (x >> 31) & 1;
  int signy = (y >> 31) & 1;
  int subsign = (sub >> 31) & 1;
  return  ((signx ^ signy) & signx) + ((signx ^ signy ^ 1) & !subsign);
}


題意解析:
x<=y? 1:0
解答過程與思考:
首先,可以通過計算t=y-x 來判定(y-x=y+(~x+1))
 int isLessOrEqual(int x, int y) {
  return !((y+(~x+1))>>31);
}
 
經過測試,發現忘記考慮計算溢位的情況(即如果X,Y符號不相同,可能出現溢位)
當x=[0x80 00 00 00] y=[0x7f ff ff ff]  sub=[0xFF FF FF FF]
當x=[0x8F FF FF FF] y=[0x7f ff ff ff]  sub=[0xF0 00 00 00]
當x=[0x7f ff ff ff] y=[0x80 00 00 00]  sub=[0x00 00 00 01]
當x=[0x7f ff ff ff] y=[0x8F FF FF FF] sub=[0x10 00 00 00]
發現 溢位後的符號位 與減數符號相同  
修正函式
int isLessOrEqual(int x, int y) {
  int sub=y+(~x+1);
  int signx = (x >> 31) & 1;
  int signy = (y >> 31) & 1;
  int subsign =!((sub >> 31) & 1);//正的話 1  負的話是0
  return  ((signx ^ signy) & signx) + ((signx ^ signy ^ 1) & subsign);
}
 
12:題目與解答
/*
 * ilog2 - return floor(log base 2 of x), where x > 0
 *   Example: ilog2(16) = 4
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 90
 *   Rating: 4
 */
int ilog2(int x) {
  int byte3 = x >> 24;
  int byte2 = (x >> 16) & 0xff;
  int byte1 = (x >> 8) & 0xff;
  int byte0 = x & 0xff;
 
  int i3 = !!byte3; /* 0: byte3=0, 1: byte3 > 0 */
  int i2 = i3 | !!byte2;
  int i1 = i2 | !!byte1;
  int i0 = i1 | !!byte0;
  int i = i3 + i2 + i1 + i0 + ~0;
  int highbyte = x >> (i << 3); /* highest byte not equal zero */
 
  int b7 = (highbyte >> 7) & 1;
  int b6 = (highbyte >> 6) & 1;
  int b5 = (highbyte >> 5) & 1;
  int b4 = (highbyte >> 4) & 1;
  int b3 = (highbyte >> 3) & 1;
  int b2 = (highbyte >> 2) & 1;
  int b1 = (highbyte >> 1) & 1;
  int b0 = highbyte & 1;//
 
  int k7 = b7;
  int k6 = k7 | b6;
  int k5 = k6 | b5;
  int k4 = k5 | b4;
  int k3 = k4 | b3;
  int k2 = k3 | b2;
  int k1 = k2 | b1;
  int k0 = k1 | b0;
  int k = k7 + k6 + k5 + k4 + k3 + k2 + k1 + k0 + ~0;
 
  return (i << 3) + k;
}


題意解析:
就是log2x=n
解答過程與思考:
對於正數 
16 0x00 00 00 10  =》0x00 00 00 04
15 0x00 00 00 0f  =》0x00 00 00 03 
額 這題實在不會 參考網路答案
 它先劃分成4個8位數值 然後依次判斷大小,並儲存
通過i來判斷從高位數起第一個1在哪個位元組裡
如果i=0  就代表 最後8位  如果i=2就是24-16位 。。
b01234567來儲存取出的8位裡的相應位值
再用k來選最高位1是哪一位  從而得出n

 
13:題目與解答
/* 
 * float_neg - Return bit-level equivalent of expression -f for
 *   floating point argument f.
 *   Both the argument and result are passed as unsigned int's, but
 *   they are to be interpreted as the bit-level representations of
 *   single-precision floating point values.
 *   When argument is NaN, return argument.
 *   Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while
 *   Max ops: 10
 *   Rating: 2
 */
unsigned float_neg(unsigned uf) {
 if (~(((uf >> 23) & 0xff) ) || !(uf & ((1 << 23) - 1)))
  	uf ^= (1 << 31);
  return uf;
}


題意解析:
如果uf是NaN 返回NaN 否則,返回-f  而且可以用if語句  那就非常簡單了
(不過這裡竟然出現了無符號float。。表示不大懂)
解答過程與思考:
0 1111 1111 0000 0000 0000 0000 0000 000 
只要後面32位中有一位不是0就是NaN
首先判斷 是否是NaN
(((uf>>23)&0xff)==0xff)&&
((uf&(0000 0000 0111 1111 1111 1111 1111 1111))!=0)  =>1
轉化=>
即 當uf不為NaN時,
~((((uf>>23)&0xff)==0xff)&&((uf&(0000 0000 0111 1111 1111 1111 1111 1111))!=0))
=>
(~((uf >> 23) & 0xff) ) || !(uf & ((1 << 23) - 1))   =》1//一個判斷 規格化 一個判斷非規格化特殊值情況

14:題目與解答
/* 
 * float_i2f - Return bit-level equivalent of expression (float) x
 *   Result is returned as unsigned int, but
 *   it is to be interpreted as the bit-level representation of a
 *   single-precision floating point values.
 *   Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while
 *   Max ops: 30
 *   Rating: 4
 */
 unsigned float_i2f(int x) {  
        unsigned shiftLeft=0;  
        unsigned afterShift, tmp, flag;  
        unsigned absX=x;  
        unsigned sign=0;  
        //special case  
    if (x==0) return 0;  
    //if x < 0, sign = 1000...,abs_x = -x  
    if (x<0)  
    {  
        sign=0x80000000;  
        absX=-x;  
    }  
        afterShift=absX;  
        //count shift_left and after_shift  
        while (1)  
        {  
                tmp=afterShift;  
                afterShift<<=1;  
                shiftLeft++;  
                if (tmp & 0x80000000) break;  
        }  
        if ((afterShift & 0x01ff)>0x0100)  
                flag=1;  
    else if ((afterShift & 0x03ff)==0x0300)  
                flag=1;  
    else  
                flag=0;  
  
        return sign + (afterShift>>9) + ((159-shiftLeft)<<23) + flag;  
} 


題意解析:
就是一個強制轉換(float)x  
解答過程與思考:
首先,考慮float與int在底層的區別
float   1 (1111 1111 )(1111 1111 1111 1111 1111 111)
int     1   111 1111 1111 1111 1111 1111 1111
顯然,要先用一個sign來儲存符號位
額,接下來就不會做了。
參考網路答案,它也首先儲存了符號位,然後對於正負進行處理,最後通過一個while迴圈與flag修正小數位和階碼位
flag考慮的是否進位

 
15:題目與解答
/* 
 * float_twice - Return bit-level equivalent of expression 2*f for
 *   floating point argument f.
 *   Both the argument and result are passed as unsigned int's, but
 *   they are to be interpreted as the bit-level representation of
 *   single-precision floating point values.
 *   When argument is NaN, return argument
 *   Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while
 *   Max ops: 30
 *   Rating: 4
 */
unsigned float_twice(unsigned uf) {  
     unsigned f=uf;  
        /* Computer 2*f. If f is a NaN, then return f. */  
    if ((f & 0x7F800000) == 0){  
        //shift one bit to left  
                f = ((f & 0x007FFFFF)<<1) | (0x80000000 & f);  
    }  
    else if ((f & 0x7F800000) != 0x7F800000){  
        /* Float has a special exponent. */  
        /* Increment exponent, effectively multiplying by 2. */  
        f =f+0x00800000;  
        }  
    return f;  
}  


題意解析:
返回兩倍float x,如果是NaN就返回NaN
解答過程與思考:
參考網路答案:
首先,考慮NaN與特殊值   x 1111 1111  xxxx xxxx xxxx xxxx xxxx  xxx 因為傳入的是unsigned  所以  符號位為0 
對於 特殊值 正無窮  0x7F 80 00 00  
0111 1111 1000 0000 0000 0000 0000 0000
兩倍正無窮  還是返回正無窮
而又因為NaN返回本身
所以 只要階碼域全為1 則返回本身
(f&0x7F800000)==(0x7F800000) return uf
再考慮  非規格化數:uf=0000 0000 0xxx xxxx xxxx xxxx xxxx xxxx
2*uf =   這個數足夠大,最後能轉化為規格化數  那麼  只需要將小數位像階碼位進位(《《1)   如果不夠大,只需要講小數位<<1   這樣的話,就可以用通式  (f & 0x007FFFFF)<<1) 來計算  ((f&0x7F800000)==0) return (f & 0x007FFFFF)<<1) 
再考慮 規格化數((f & 0x7F800000) != 0x7F800000)  因為這個IF語句在後邊,所以不用考慮 非規格化值
uf=0xxx xxxx x xxx xxxx xxxx xxxx xxxx xxxx
只需要在階碼域加1  即 f+0x00800000  
最後,至於為什麼會有 | (0x80000000 & f);  這一句,好像是因為排除負數影響 可是,這不是傳入一個unsigned 數值麼?  這裡還不是很明白 
如果刪去後面半句話,程式會報錯