《深入理解计算机系统》Lab1-data lab

前言

开始用博客记录学习过程啦,如果文章有什么问题,恳请指正~
本文内容是《深入理解计算机系统》——data lab的代码及其说明



提示:以下是本篇文章正文内容,下面案例可供参考

一、该实验要我们做什么?

理解说明文档很是花了我一点时间,简而言之
1.补充完整bits.c文件中的15个函数,限制条件是有限数量、规定的操作符(在每一个函数前有特别注释说明);
在这里插入图片描述

2.用dlc文件测试操作符是否符合要求。

./dlc bits.c

3.用btest文件测试函数功能是否符合要求。

make btest
./btest -f bitAnd//单个测试bitAnd函数(建议写一个函数测一个)
./btest  //测试所有函数

二、题解及其说明

1.bitAnd

  • 要求:
/* 
 * bitAnd - x&y using only ~ and | 
 *   Example: bitAnd(6, 5) = 4
 *   Legal ops: ~ |
 *   Max ops: 8
 *   Rating: 1
 */
  • 分析:x&y=~~(x&y)=(x|~y)
  • 题解:
int bitAnd(int x, int y) {
	int and=~(~x|~y);
	return and;
}
  • 总结:德摩根律的应用

2.getByte

  • 要求:
/* 
 * 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
 */
  • 分析:
    1.输入4个字节从左往右依次编号为3、2、1、0。想要提取字节则必须把所需要的字节移至最右边,
    2.经画图发现,n为多少则右移几个字节即可;
    3.一个字节为8位,右移n个字节需要右移n*8位,可表示为n<<3;
    4.1&x=x;0&x=0,故将右移后的结果与&0xff想与,即可使前三个字节清零,而最后一个字节保持不变
  • 题解:
int getByte(int x, int n) {
	return x>>(n<<3)&0xff;
}
  • 总结:
    1.n>>3 相当于n/8 n<<2 相当于n*4
    2.一个字节有8位
    3.1&x=x;0&x=0

3.logicalShift

  • 要求:
/* 
 * 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 
 */
  • 分析:

  • 题解:
    1.C语言默认用的是算术右移,算术右移是符号位(0/1)补位,而题目要求的是逻辑右移,逻辑右移是0补位。故可以先进行算术右移,然后将补位的数字(0/1)全部替换成0;
    2.替换可以使用掩码(参考函数2)。使其前n位为0,后(32-n)位为1;
    3.掩码的构造方法。
    \quad a. 将1左移31位:1<<31; 10000000….
    \quad b. 取反:~(1<<31) 0111111….
    \quad c. 右移n位(符号位是0,0补位):(~(1<<31))>>n 0000011111….
    \quad d. 左移1位(注意:题目要求不能用减法,故不直接使用右移n-1位):
    \quad ((~(1<<31))>>n)<<1 \quad 00001111….110
    \quad e. 和1相或以补最右边的0:(((~(1<<31))>>n)<<1) 00001111….111
    4.将右移n位后的x与掩码相与

int logicalShift(int x, int n) {
	int mask_code=(((~(1<<31))>>n)<<1)|1;
	return (x>>n)&mask_code;
 	
}
  • 总结:
    1.算术右移符号位补位,逻辑右移0补位,C语言默认是算术右移
    2.掌握掩码的构造方法

4.bitCount

  • 要求:
/*
 * bitCount - returns count of number of 1's in word
 *   Examples: bitCount(5) = 2, bitCount(7) = 3
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 40
 *   Rating: 4
 */
  • 分析:
    最开始的思路:
    1.1&x,可用于判断x的最后一位是否为1。
    2.通过将x不断右移,遍历x的每一位。
    但是明显超过了题目要求的操作数40

查阅资料后发现可以用分治法来做

  1. 求32位二进制数里有多少个1,可以先2位2位的看,再4位4位的看,再8位8位的看,再16位16位的看,最后看32位。
  2. 举例说明:x=10110100(8位)
    第一步:shift_2-1=(10|11|01|00)&01010101=00010100
    X右移1位:shift_2-2=(01|01|10|10)&01010101=01010000
    Sum_2=shift_2-1+shift_2-2=00010100
    +01010000
    =01|10|01|00
    第二步:shift_4-1=(0110|0100)&00110011=0010|0000
    X右移2位:shift_4-2=(0001|1001)&00110011=0001|0001
    Sum_2=shift_4-1+shift_4-2=00100000
    +00010001
    =0011|0001
    第三步:shift_8-1=(00110001)&00001111=00000001
    X右移4位:(00000011)&00001111=00000011
    Sum_2=shift_2-1+shift_2-2=00000001
    +00000011
    =00000100
    即含有4个1
  3. 因为实验要求中整数常数的范围是:0-255(0x0-0xff)
    故需要对掩码进行转换,这可以通过移位和或运算实现
  • 题解:
int bitCount(int x) {
       //构造掩码
	int m_1,m_2,m_4,m_8,m_16;
	m_1=0x55|(0x55<<8);//01010101=0x55
	m_1=m_1|(m_1<<16);
	m_2=0x33|(0x33<<8);//00110011=0x33
	m_2=m_2|(m_2<<16);
	m_4=0x0f|(0x0f<<8);//00001111=0x0f
	m_4=m_4|(m_4<<16);
	m_8=0xff|(0xff<<16);//11111111=0xff
        m_16=0xff|(0xff<<8);
	x=(x&m_1)+((x>>1)&m_1);
	x=(x&m_2)+((x>>2)&m_2);
	//最多32个1,故下面三组的最高位不会是1
	x=(x&m_4)+((x>>4)&m_4);
	x=(x&m_8)+((x>>8)&m_8);
	x=(x&m_16)+((x>>16)&m_16);
	return x;
}
  • 总结:
    1.用0101…来2位两位计算1的个数,用00110011…来4位4位计算1的个数,用…
    2.有限循环数可以用>>配合|来获得

5.bang

  • 要求:
/* 
 * bang - Compute !x without using !
 *   Examples: bang(3) = 0, bang(0) = 1
 *   Legal ops: ~ & ^ | + << >>
 *   Max ops: 12
 *   Rating: 4 
 */
  • 分析:
    1.!x的结果只能为1/0
    2.求反的过程实际就是判断x是否为0。x若为0,则!x为1,否则为0;
    3.只有0的原码和补码的或的最高位为0,即转化为判断原补码或运算后的最高位是否为0的问题,而这可以用右移31位后&1来判断

  • 题解:

int bang(int x) {
	int com=~x+1;//求补码
	return ~((com|x)>>31)&1; //只有0的原补码的或的最高位为0
}
  • 总结:
    1.只有0的原码和补码的或的最高位为0
    2.~和!不同。~是按位求反;!是逻辑求反,其结果只有0或1。

6.tmin

  • 要求:
/* 
 * tmin - return minimum two's complement integer 
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 4
 *   Rating: 1
 */
  • 分析:最小的二进制补码是100000000000000000000000000000000(负数)
  • 题解:
int tmin(void) {
  return 1<<31;
}
  • 总结:符号位为1表示负数,后面的数字越小,其绝对值越大,其值越小(和正数相反)

7.fitsBits

  • 要求:
/* 
 * 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
 */
  • 分析:
    1.题目要求判断x能否用n位二进制数表示,而x肯定能用32位二进制数表示,可通过将32位二进制数左移(32-n)位再右移(32-n)位后的数是否还和原来相等来判断(由于补位原则,相当于只保留了n位有效数)
    2.因为题目不允许用-号,故可以用+上n的反码再+1的方式求差
    3.通过异或^来判断是否相等

  • 题解:

int fitsBits(int x, int n) {
	//int com=~x+1;
	int count=32+(~n)+1;
	return !(x^(x<<count>>count));
  return 2;
}
  • 总结:
    1.a-b=a+(~b)+1;
    2.用异或^来判断两个数是否相同,相同为0,不同为1
    3.算术右移:最高位填充符号位,正数填0,负数填1
    逻辑右移:都是填0
    左移都是填0

8.divpwr2

  • 要求:
/* 
 * 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) {
	int sign,bias;	
	sign=x>>31;
	bias=sign&((1<<n)+(~0));   //偏执位 正数时为0
	return (x+bias)>>n;
}
  • 总结:正数除法向0取整,负数除法向负取整

9.negate

  • 要求:
/* 
 * negate - return -x 
 *   Example: negate(1) = -1.
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 5
 *   Rating: 2
 */
  • 分析:-x=~x+1
int negate(int x) {
  return (~x)+1;
}
  • 总结:-x=~x+1

10.isPositive

  • 要求:
/* 
 * isPositive - return 1 if x > 0, return 0 otherwise 
 *   Example: isPositive(-1) = 0.
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 8
 *   Rating: 3
 */
  • 分析:
    1.正数的符号位为0,负数的符号位为1,0的符号位为0。
    2.判断正数:符号位为0
    3.判断0:!0=1 !1=0 !-1=1
    4.x|0=x,x|1=1;

  • 题解:

int isPositive(int x) {
	return !((x>>31)|(!x));
}
  • 总结:
    1.!0=1 !1=0 !(-1)=1 只有0的!为1,故可以用来判断是否为0
    2.x|0=x,x|1=1;

11isLessOrEqual

  • 要求:
/* 
 * isLessOrEqual - if x <= y  then return 1, else return 0 
 *   Example: isLessOrEqual(4,5) = 1.
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 24
 *   Rating: 3
 */
  • 分析:
  1. 可以将判断x<=y转化为x-y<=0,其中x-y=x+(~y)+1。
  2. 差<0 返回1
    差>=0 返回0
  3. 当x,y同号时:用&1判断符号位即可
    当x,y异号时:可能会发生溢出,需要判断两种数的符号位
  • 题解:
int isLessOrEqual(int x, int y) {
	int signx=(x>>31)&1;
	int signy=(y>>31)&1;
	int signy_x=((y+~x+1)>>31)&1;
	return (!(signx^signy)&!signy_x)|((signx^signy)&signx); //同号|异号
}
  • 总结:做减法时需考虑两个数是否异号,异号可能发生溢出。

12.ilog2

  • 要求:
/*
 * ilog2 - return floor(log base 2 of x), where x > 0
 *   Example: ilog2(16) = 4
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 90
 *   Rating: 4
 */
  • 分析:x&y=~~(x&y)=(x|~y)
  • 题解:
    在这里插入图片描述
int ilog2(int x) {
	int sign,shift1,shift2,shift3,shift4,shift5;
	//16|16
	sign=!!(x>>16);
	shift1=sign<<4;
	x=x>>shift1;
	//24|8
	sign=!!(x>>8);
	shift2=sign<<3;
	x=x>>shift2;
	//28|4
	sign=!!(x>>4);
	shift3=sign<<2;
	x=x>>shift3;
	//30|2
	sign=!!(x>>2);
	shift4=sign<<1;
	x=x>>shift4;
	//31|1
	sign=!!(x>>1);
	shift5=sign;
	return shift1+shift2+shift3+shift4+shift5;
}
  • 总结:大范围问题二分法求解

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
 */
  • 分析:
  1. 题目翻译:题目的要求就是更改一下浮点数的符号位,特别需要注意的是当参数为NaH的时候返回参数本身
  2. IEEE浮点表示(32bits):
    |s (符号位,1位) |exp(阶码,8位)| Frac(尾码,23位)|
  3. 通过判断exp是否为11111111,frac是否不等于000…来判断参数是否为NaH(不是一个数)
  4. 通过^改变符号位
  • 题解:
unsigned float_neg(unsigned uf) {
	int exp=(uf<<1)>>24;//左移一位以去除符号位
	if(exp==0xff)
	{
		if(uf<<1!=0xff000000) return uf;
	}
	return uf^(1<<31);
}
  • 总结:
    exp的几种特殊情况
    exp=00000000:阶码E=-Bias+1; 尾数M=0.xxx…
    exp=11111111,frac=000…0:s=1时表示负无穷s=0时表示正无穷
    exp=11111111,frac!=000…0:NaN、一些无法表示的数

14.float_i2f

  • 要求:
/* 
 * 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
 */
  • 分析:
    1.题目要求将整数表示为浮点数(IEEE),需要分为三个部分来处理:符号位、阶码、尾码
    2.如果参数为0,直接返回0
    3.如果参数为负数,因为负数在内存中是以补码形式保存的,而输出要求是无符号数,故需要先转换为其绝对值
    4.使用无符号数的原因:无符号数算术左移补0
    5.符号位:直接将最高位&(1<<31)
    6.尾码:先左移使其最高位为1,并记录移位个数shift_count(以此计算阶码),再左移移位舍弃最高位,最后右移9位并采用向偶数舍入(https://blog.csdn.net/qq_34369618/article/details/52247350)得到尾码
    7.阶码:E=32-shift_count-1 EXP=127+E=159- shift_count

  • 题解:

unsigned float_i2f(int x) {
	unsigned sign,shift_count,tail,rank,flag,temp;
	if(!x) return 0; //参数为0直接返回0
	sign=x&(1<<31);  //取得符号位
	if(sign) x=-x; //参数为负数则转化为其绝对值
	shift_count=0;
	tail=x;
	while(1)
	{
		temp=tail;
		tail<<=1;  //tail多左移一次,最终保存的是去除首位1后的数
		shift_count++;
		if(temp&0x80000000) break; //temp最终保存的是最高位为1的数
	}
	//tail<<=1; //去除首位1
	if((tail&0x01ff)>0x0100) flag=1;
	else if((tail&0x03ff)==0x0300) flag=1;
	else flag=0;
	tail=(tail>>9)+flag;
	rank=(127+32-shift_count)<<23;
	return sign+rank+tail;
}
  • 总结:
    1.浮点数向偶数舍入:
    \quad 例如有效数字超出规定数位的多余数字是1001,它大于超出规定最低位的一半(即0.5),故最低位进1。如果多余数字是0111,它小于最低位的一半,则舍掉多余数字(截断尾数、截尾)即可。对于多余数字是1000、正好是最低位一半的特殊情况,最低位为0则舍掉多余位,最低位为1则进位1、使得最低位仍为0(偶数)。
    2.无符号数算术右移补0,有符号负数算术右移补1

15.float_twice

  • 要求:
/* 
 * 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
 */
  • 分析:
    情况一:exp=00000000,参数为非规格化数,尾数左移一位(保留符号位)即可
    情况二:exp=111111111,参数是NaN,直接返回即可
    情况三:阶码不全为0也不全为1,浮点数×2只需阶码加一位

  • 题解:

unsigned float_twice(unsigned uf) {
	if((uf&0x7f800000)==0) return (uf&0x7fffff)<<1|(uf&(1<<31));
	else if((uf&0x7f800000)==0x7f800000) return uf;
	else return uf+0x800000;
}
  • 总结:做浮点数题目时首先考虑阶码全为1,全为0,其他 三种情况

三、总体测试

在这里插入图片描述
在这里插入图片描述

  • 8
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答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 语言数据类型的细节和位运算的机器级实现有一定的了解。例如,我们要了解有符号整数和无符号整数的区别,以及它们在机器上的表示方式。我们还需要理解位运算的计算过程,包括位移的规则、符号扩展和逻辑运算等。 通过这个实验,我们可以更深入理解计算机的工作原理,学会用底层的方式实现高级的运算,掌握常用的位运算技巧。这对于提高编程的效率和代码的可读性都有很大帮助。同时,这也为后续的计算机科学学习打下了坚实的基础。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值