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) 很容易解决

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 数值么?  这里还不是很明白 
如果删去后面半句话,程序会报错


  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值