CSAPP DATA LAB1————位運算

計算機組成位運算實驗,寫的比較詳細。
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) 很容易解決
驗證:
這裏寫圖片描述
成功編譯測試,得到1分 說明程序成功
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<

/* 
 * 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<

* 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 數值麼? 這裏還不是很明白
如果刪去後面半句話,程序會報錯
這裏寫圖片描述
驗證:

整個程序包驗證:
這裏寫圖片描述
這裏寫圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章