深入理解计算机系统实验二datalab

实验目的:
datalab实验提供了一个文件夹,我们的目的只是改写bits.c中的15个函数,使其完成相应的功能即可。至于其他文件是用来编译、测试,并且限制你使用一些禁止的运算符。

实验说明:
一、不允许允许的操作
① 使用任何控制结构,如if, do, while, for, switch等。
② 定义或使用任何宏。
③ 在此文件中定义任何其他函数。
④ 调用任何库函数。
⑤ 使用任何其他的操作,如&&, ||, -, or ?:
⑥ 使用任何形式的casting
⑦ 使用除int以外的任何数据类型。这意味着你不能使用数组、结构等。
(浮点运算的规则没那么严格,具体看每个函数上方的规则说明)
二、实验环境
将整个文件拖入Linux环境32位环境中,(博主使用的是VM平台,安装VM tools后可随意拖拽文件进虚拟机),打开终端;
1.make clean 清除所有编译文件
2.make 编译所有文件 (如果bits.c有问题编译不成功)
3…/btest bits.c 测试bits.c
最终结果:(函数正确情况下)
在这里插入图片描述
三、函数编写

1.bitAnd

题目: 只能用~和|来实现位的与操作。

题目要求:
/*
bitAnd - x&y using only ~ and |
Example: bitAnd(6, 5) = 4
Legal ops: ~ |
Max ops: 8
Rating: 1
*/
思路:

可以通过摩根定律得到AB=!(!A+!B)

代码:

int bitAnd(int x, int y) {//使用~与|实现按位与
  return ~(~x|~y);

2.getByte

题目: 给定n (0<=n<=3),求出第n个字节是哪数字。

题目要求:
/*
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
*/
思路:

右移n*8位,把前面的24位变成0即可(&0xff)

代码:

int getByte(int x, int n) {//获取x的第n位字节

  return ((x>>(n<<3))&0xff);

}

3.logicalShift

题目: 将x按逻辑右移移动n(0<=n<=31) 位。

题目要求:
/*
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型右移默认算术即高位补符号位,unsigned型默认逻辑右移即补0
只要构造出00……0111111(前面n-1个0,后面全是1的数)与算术右移结果相与即可

代码:

int logicalShift(int x, int n) {//将32位数逻辑右移n位
  return ~((((1<<31)&x)>>n)<<1)&(x>>n);
}

4.bitCount

题目: 用位运算计算出x中有多少个1

题目要求:
/*
bitCount - returns count of number of 1’s in word
Examples: bitCount(5) = 2, bitCount(7) = 3
Legal ops: ! ~ & ^ | + << >>
Max ops: 40
Rating: 4
*/
思路:

分治的思想:构造
0x55555555 偶数位与前一位奇数位(右移一位对齐相加)相加保存在count
0x33333333 count右移两位对齐与本身相加
0x0f0f0f0f count右移四位对齐与本身相加
0x00ff00ff count右移八位对齐与本身相加
0x0000ffff count右移16位对齐与本身相加
输出即可

代码:

int bitCount(int x) {//计1个数
    int count;
    int a = (0x55)|(0x55<<8);
    int count2 = (a)|(a<<16);
    int b = (0x33)|(0x33<<8);
    int count4= (b)|(b<<16);
    int c = (0x0f)|(0x0f<<8);
    int count8 = (c)|(c<<16);
    int count16 = (0xff)|(0xff<<16);
    int count32 = (0xff)|(0xff<<8);
    count = (x&count2)+((x>>1)&count2);
    count = (count&count4)+((count>>2)&count4);
    count = (count  + (count >> 4)) & count8;
    count = (count + (count >> 8)) & count16;
    count = (count + (count >> 16)) & count32;
    return count;
}

5.bang

题目: 不能用!运算符求出!x结果

题目要求:
/*
bang - Compute !x without using !
Examples: bang(3) = 0, bang(0) = 1
Legal ops: ~ & ^ | + << >>
Max ops: 12
Rating: 4
*/
思路:

判断是不是0即可,然而0的特殊性,0的相反数仍然是0

代码:

int bang(int x) {//取非

  return (~(((~x+1)|x)>>31)&1);
}

6.tmin

题目: 返回补码整数的最小整数数值

题目要求:
/*
tmin - return minimum two’s complement integer
Legal ops: ! ~ & ^ | + << >>
Max ops: 4
Rating: 1
*/
思路:

补码的最最小值为100……0后面全是0

代码:

int tmin(void) {//最小的二进制数
  return 1<<31;
}

7.fitsBits

题目:给出n个二进制位,能否表示x。

题目要求:
/*
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
*/
思路:

如果是一个正数或零的话,且能用相应的补码表示,则向右移动n-1位之后,此时该数的数值一定为0,如果该数是一个负数的话,如果移动n-1位后,该数的数值应该一定等于为-1,所以让x右移n-1位然后判断是否全是0或者全是1便可以。

代码:

int tmin(void) {//最小的二进制数
  return 1<<31;
}

8.divpwr2

题目:给出整数x,整数n,求[x/(2^n)],答案要接近趋向0方向。

题目要求:
/*
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型的a和b,a/b向下取整,比如17/16,区间为(1,2),向下取整为1,而对于负数如果-17/16区间为(-2,-1),向下取整则出现错误。所以x为负数是加上偏移量即可

代码:

int divpwr2(int x, int n) {//x/n(向下取整数)
    return (x+((x>>31)&((1<<n)+(~0))))>>n;
}

9.negate

题目:求x的相反数

题目要求:
/*
negate - return -x
Example: negate(1) = -1.
Legal ops: ! ~ & ^ | + << >>
Max ops: 5
Rating: 2
*/
思路:

按位取反+1

代码:

int negate(int x) {//取反
  return ~x+1;
}

10.isPositive

题目:判断x是不是正数

题目要求:
/*
isPositive - return 1 if x > 0, return 0 otherwise
Example: isPositive(-1) = 0.
Legal ops: ! ~ & ^ | + << >>
Max ops: 8
Rating: 3
*/
思路:

判读符号位即可,还要看看是不是0

代码:

int isPositive(int x) {//x>1返回0
   return !(x>>31|(!x));
}

11.isLessOrEqual

题目:判定x<=y,如果是就返回1,如果不是就返回0。

题目要求:
/*
isLessOrEqual - if x <= y then return 1, else return 0
Example: isLessOrEqual(4,5) = 1.
Legal ops: ! ~ & ^ | + << >>
Max ops: 24
Rating: 3
*/
思路:

当相同符号时,直接相减判断是否为负数即可。
当前者为正,后者为负时,直接返回0,前者为负后者为正时返回1。

代码:

int isLessOrEqual(int x, int y) {//判断x<=y?
  int signx = (x >> 31) &0x1;       //取x的符号位
   int signy = (y >> 31) &0x1;       //取y的符号位
   int isSameSign=! (signx ^ signy) ;// 对符号位异或取反,判断是否相同
   int p=! ( ( (~x)+1+y ) >> 31);    //计算y-x,并取结果的符号位

   return ( isSameSign & p ) | ( ( !isSameSign ) & signx);
}

12.ilog2

题目:求整数的log(x)
题目要求:
/*
ilog2 - return floor(log base 2 of x), where x > 0
Example: ilog2(16) = 4
Legal ops: ! ~ & ^ | + << >>
Max ops: 90
Rating: 4
*/
思路:

该题和之前那道求1的个数类似,也需要采用二分的思想,先判断第一个1在前16位还是后16位,记录下来,再判断再前8位还是后8位,一直做到判断在前1位还是后1位,便是最终答案。公式log(x)=16a+8b+4c+2d+e。那么count=abcde。因为x长32位,首先我们先将x>>16,判断高16位是不是还>0,如果>0,!(x>>16)就是0,我们要将他转换到a的位置就是将!!(x>>16)再次取非是1,然后<<4,到a的位置,就说明这个数大于16,1肯定在高16位处,然后在接着将高位折半到8位,就是>>8+16,看看高8位是不是也是>0。依次进行下去直到判断前一位还是后一位……(此处应该为无符号数)

代码:

int ilog2(int x) {//取log(x),多少位2进制才能表示
  int bitsNumber=0;
    bitsNumber=(!!(x>>16))<<4;
    bitsNumber=bitsNumber+((!!(x>>(bitsNumber+8)))<<3);
    bitsNumber=bitsNumber+((!!(x>>(bitsNumber+4)))<<2);
    bitsNumber=bitsNumber+((!!(x>>(bitsNumber+2)))<<1);
    bitsNumber=bitsNumber+(!!(x>>(bitsNumber+1)));
    return bitsNumber;
}

13. float_neg

题目:浮点数取反
题目要求:
/*
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
*/
思路:

直接将符号位改反即可,但需要判断NAN的情况,这里已经是浮点数类型了,可以使用if判断。

代码:

unsigned float_neg(unsigned uf) {//对浮点取反
 unsigned result;
    unsigned tmp;
    result=uf^0x80000000; //将符号位改反 -f
    tmp=uf & (0x7fffffff);
    if(tmp > 0x7f800000)//此时是NaN
        result = uf;
    return result;
}

14. float_i2

题目:浮点数取反
题目要求:
/*
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
*/
思路:

将整形转化为无符号浮点数,即求浮点数。先取的符号位,再将剩余部分全部取为正数形式,即absx,即可以得到无符号的数值。然后将有数字的部分直接移动到最高位,记录移动的位数,再将其移动9位(因尾数只要23即可)。对于阶码部分,由于记录的是小数点从31位右数到第一个1,但实际上需要处理的是从第0位到第一位,所以E=32-shiftleft,bias为127,加上为159,if部分做舍入处理

代码:

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;
}

15. float_twice

题目:浮点数乘以2倍。
题目要求:
/*
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
*/
思路:

将无符号浮点数乘2,对无阶码小数,对其尾部乘2即可,即直接左移一位,但要提前记录符号位。对于规格化数,直接对其阶码+1即可

代码:

unsigned float_twice(unsigned uf) {//浮点*2
  unsigned f = uf;
    if ((f & 0x7F800000) == 0) //
    {
        //左移一位
        f = ((f & 0x007FFFFF) << 1) | (0x80000000 & f);//007fff---11111111111111111111111 23bit
    }
    else if ((f & 0x7F800000) != 0x7F800000)
    {
        f =f + 0x00800000;  //100000   23
    }
    return f;
}
  • 10
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 好的,这里是您要求的回答: 这个问题涉及到计算机系统的实验课程——深入理解计算机系统(CS:APP)的第一个lab任务,也称为Data Lab。这个任务要求学生使用C语言编写函数来实现不同的位级操作,从而深入理解计算机中数字的存储和运算。 ### 回答2: 在Data Lab的第部分中,我们学习了如何用不超过十六个操作来实现不同的位运算。这些操作包括移位,逻辑运算,比较和掩码等。通过这些操作的实现,我们可以进一步理解底层计算机系统中位运算的实现原理。 在实现这些操作时,我们需要了解底层计算机系统的运算和数据类型。如符号扩展、零扩展和反码等。同时,在编写代码时需要熟练使用位运算的操作符号以及一些基本控制流语句如循环、条件语句等。 除了实现这些基本操作,我们也需要应用这些操作来解决一些实际问题。例如,实现一个函数,将一个十六进制数按位翻转,或是计算一个整数进制表示中1的数量等。 通过Data Lab的学习,我们深入了解了计算机系统中底层的位运算实现原理,并学会了如何用简洁高效的代码实现这些操作。同时,这些操作也常常被用在各种领域的计算机编程中,对于未来的学习与工作都有很大的帮助。 ### 回答3: 在《深入理解计算机系统lab1:data lab)中,我们主要学习了位运算和两个的补码表示。这些知识对于我们了解计算机的原理和编码方式非常重要。 在这个实验中,我们需要完成一些与位运算相关的任务。其中包括: 1. 实现位运算。我们需要用 C 语言实现一些常见的位运算,如与、或、非、异或、左移、右移等。 2. 计算 x 的相反数。 3. 检查 x 是否为零。 4. 判断 x 的符号是否为负数。 5. 计算 x 和 y 的和,但不能使用加法运算。 为了完成这些任务,我们需要对 C 语言数据类型的细节和位运算的机器级实现有一定的了解。例如,我们要了解有符号整数和无符号整数的区别,以及它们在机器上的表示方式。我们还需要理解位运算的计算过程,包括位移的规则、符号扩展和逻辑运算等。 通过这个实验,我们可以更深入地理解计算机的工作原理,学会用底层的方式实现高级的运算,掌握常用的位运算技巧。这对于提高编程的效率和代码的可读性都有很大帮助。同时,这也为后续的计算机科学学习打下了坚实的基础。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值