CSAPP实验一:DataLab

这篇具有很好参考价值的文章主要介绍了CSAPP实验一:DataLab。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Lab1 DataLab

这个实验主要是让大家充分理解C语言中位运算、逻辑运算和算数运算以及计算机中无符号数、有符号数和浮点数是如何表示的。
通过认真阅读完CSAPP第三版中第二章的内容,完成实验难度不是太大,部分题目比较有难度~

/datalab-handout这个目录下我们只用修改bits.c这个文件里的代码即可。
dlc是帮助我们检查我们写的代码是否合法,是否按照题目要求使用了规定的运算符并且是否超出规定的运算符的数目,通过执行命令./dlc bits.c即可运行。

每次运行时,可以通过以下指令进行:

make clean
make btest
./btest

每道题的要求不同,在编写代码时务必仔细阅读每道题的要求!

1. bitXor

这个函数要求我们只用~&两个运算符来实现异或运算。
我们可以将异或运算写出来:

a ⊕ b = ( a ∧ ¬ b ) ∨ ( ¬ a ∧ b ) a \oplus b = (a \land \lnot b) \lor (\lnot a \land b) ab=(a¬b)(¬ab)

利用德摩根律:

a ⊕ b = ¬ ( ¬ ( a ∧ ¬ b ) ∧ ¬ ( ¬ a ∧ b ) ) a \oplus b = \neg (\neg (a \land \lnot b) \land \lnot (\lnot a \land b)) ab=¬(¬(a¬b)¬(¬ab))
现在就只剩下~&运算符了,并且数目没有超过14个,该公式也可以进一步化简。

//1
/* 
 * bitXor - x^y using only ~ and & 
 *   Example: bitXor(4, 5) = 1
 *   Legal ops: ~ &
 *   Max ops: 14
 *   Rating: 1
 */
int bitXor(int x, int y) {
    return ~(~(~x & y) & ~(x & ~y)); 
}
2. tmin

这个函数要求我们返回二进制形式下,补码表示的最小数。

这个数显然是0x80000000,因为题目要求常数不能超过255,所以我们最好就不写1 << 31了。

/* 
 * tmin - return minimum two's complement integer 
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 4
 *   Rating: 1
 */
int tmin(void) {
	return (0x80 << 24);
}
3. isTmax

这个函数让我们判断一个数是不是二进制形式下,补码表示的最大数。

这个数是0x7fffffff

通过观察,我们发现这个数取反后,就是tmin;而tmin有一个很好的特点就是,它和它的相反数一样。

但是,值得注意的是,0也和它的相反数表示形式一样,所以还需要特判一下这个数不是0

/*
 * isTmax - returns 1 if x is the maximum, two's complement number,
 *     and 0 otherwise 
 *   Legal ops: ! ~ & ^ | +
 *   Max ops: 10
 *   Rating: 1
 */
int isTmax(int x) {
	x = ~x;
	return !(x ^ (~x + 1)) & (!!x);
}
4. allOddBits

这个函数让我们判断一个给定的数在二进制表示下,是不是全部奇数位都是1.

这个很容易实现,我们可以先生成一个奇数位都是1,偶数位都是0的掩码mask。

然后让x & mask,看看mask是否保持不变,如果不变,说明满足条件。

/* 
 * allOddBits - return 1 if all odd-numbered bits in word set to 1
 *   where bits are numbered from 0 (least significant) to 31 (most significant)
 *   Examples allOddBits(0xFFFFFFFD) = 0, allOddBits(0xAAAAAAAA) = 1
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 12
 *   Rating: 2
 */
int allOddBits(int x) {
	int mask = (0xAA << 8) | 0xAA;
	mask = (mask << 16) | mask;
	return !((x & mask) ^ mask);
}
5. negate

这个函数简单,让我们返回一个数的相反数。

直接~x + 1即可。

/* 
 * negate - return -x 
 *   Example: negate(1) = -1.
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 5
 *   Rating: 2
 */
int negate(int x) {
  	return (~x + 1);
}
6. isAsciiDigit

这个函数让我们判断一个数是否位于0x300x39之间。

我们可以分别进行判断,首先判断10位是否为3,我们可以利用右移运算符。

然后,判断个位是否小于等于9

我们判断a <= b时,可以通过a + b <= 0来进行判断,也就是让二者相加,然后判断符号位。

/* 
 * isAsciiDigit - return 1 if 0x30 <= x <= 0x39 (ASCII codes for characters '0' to '9')
 *   Example: isAsciiDigit(0x35) = 1.
 *            isAsciiDigit(0x3a) = 0.
 *            isAsciiDigit(0x05) = 0.
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 15
 *   Rating: 3
 */
int isAsciiDigit(int x) {
	int y = x & 0xf; // 把最低位先拿出来
  	 return !((x >> 4) ^ 0x3) & !((0x9 + (~y + 1)) >> 31);
}
7. conditional

这个函数让我们实现一个条件表达式 x ? y : z

只要x不为0,我们就返回y;否则返回z

首先,设置一个变量notZero来判断x是否不为0,如果不为0,则该变量为1;否则为0

然后,设置一个变量flag,当notZero1时,该变量为0xffffffff;否则为0x00000000

此时,我们就可以很巧妙地利用(flag & y) | (~flag & z)来进行计算了。

/* 
 * conditional - same as x ? y : z 
 *   Example: conditional(2,4,5) = 4
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 16
 *   Rating: 3
 */
int conditional(int x, int y, int z) {
  	int notZero = !!x;
	int flag = ~notZero + 1;
	return (flag & y) | (~flag & z);

}
8. isLessOrEqual

这个函数让我们判断 x ≤ y x \le y xy x = y x = y x=y很容易判断,因此我们先判断 x = y x = y x=y

判断 x < y x < y x<y的时候,我们仍然使用 x + y < 0 x + y < 0 x+y<0这个条件来判断。

但是要注意,可能会出现 x + y x + y x+y溢出的情况,从而导致判断错误。

因此,我们首先根据符号进行判断,如果二者符号不同,那么答案显然;否则再利用上式进行判断。

/* 
 * 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 flagx = (x >> 31 & 1);
	int flagy = (y >> 31 & 1);
	int notZero = !!(flagx ^ flagy);
	int flag = ~notZero + 1;
	int less = ((x + (~y + 1)) >> 31 & 1);
	return (!(x ^ y)) | ((flag & flagx) | (~flag & less));
}
9. logicalNeg

这个函数让我们实现!运算符——也就是x = 0时返回1,否则返回0

因此,我们可以通过判断x是否为0来实现这个函数,不难发现,只有0和其相反数或之后,全部都位为0

所以,我们利用这个条件判断即可。

/* 
 * logicalNeg - implement the ! operator, using all of 
 *              the legal operators except !
 *   Examples: logicalNeg(3) = 0, logicalNeg(0) = 1
 *   Legal ops: ~ & ^ | + << >>
 *   Max ops: 12
 *   Rating: 4 
 */
int logicalNeg(int x) {
  	return ((x | (~x + 1)) >> 31) + 1;
}
10. howManyBits

这个函数让我们判断一个数在二进制补码表示下,最少需要多少位才能表示出来。

通过观察给的样例:

12:01100

-5:1011

0:0

-1:1

x为正数时,假设idx表示为1的最高的位置,答案即idx + 1

x为负数时,我们将x取反,假设idx表示此时为1的最高的位置,答案即idx + 1

因此,我们可以通过找出最高的1的位置来计算答案。

因为不能使用循环,所以我们利用二分查找。

首先,判断高16位是否存在1,如果存在1的话,我们就只需要在高16位上寻找最高位;如果高16位不存在1,那么我们去低16位寻找。就这样一直找下去,直到区间长度变为1停止。

具体实现可以看代码。

/* howManyBits - return the minimum number of bits required to represent x in
 *             two's complement
 *  Examples: howManyBits(12) = 5
 *            howManyBits(298) = 10
 *            howManyBits(-5) = 4
 *            howManyBits(0)  = 1
 *            howManyBits(-1) = 1
 *            howManyBits(0x80000000) = 32
 *  Legal ops: ! ~ & ^ | + << >>
 *  Max ops: 90
 *  Rating: 4
 */
int howManyBits(int x) {
	int b16, b8, b4, b2, b1, b0;
	int sign = x >> 31;
	x = (sign & ~x) | (~sign & x);
	b16 = !!(x >> 16) << 4;
	x = x >> b16;
	b8 = !!(x >> 8) << 3;
	x = x >> b8;
	b4 = !!(x >> 4) << 2;
	x = x >> b4;
	b2 = !!(x >> 2) << 1;
	x = x >> b2;
	b1 = !!(x >> 1);
	x = x >> b1;
	b0 = x;
  	return b16 + b8 + b4 + b2 + b1 + b0 + 1;
}
11. floatScale2

从这个函数开始,就都是涉及到浮点数表示了,如果不是很了解,建议仔细阅读一下《深入理解计算机系统》第二章后半部分的内容。

这个函数给了我们一个浮点数表示的数f,让我们计算出2 * f的浮点数表示。

首先,我们把f中的符号位s,阶数exp,尾数frac全都扣出来。

然后,判断一下是否是非规格化的值,也就是判断阶数exp是否为0,如果为0,我们直接把尾数乘2然后配上符号输出即可。

如果exp = 255,也就是为NaN或者无穷,我们直接返回f

否则,就把阶数exp加1,如果此时exp为255,也就是阶码位全为1,此时溢出了,我们返回无穷。

最后,代表没有溢出,我们返回正常的值即可。

//float
/* 
 * floatScale2 - 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 floatScale2(unsigned uf) {
  	unsigned s = uf & (1 << 31);
	unsigned exp = (uf & 0x7f800000) >> 23;
	unsigned frac = uf & (~0xff800000);

	if(exp == 0) return frac << 1 | s;
	if(exp == 255) return uf;
	exp ++;
	if(exp == 255) return 0x7f800000 | s;
	return s | (exp << 23) | frac;
}
12. floatFloat2Int

这个函数让我们把一个给定的浮点数f通过强制类型转换为int

int的表示范围要比单精度浮点数小很多 [ − 2 31 , 2 31 − 1 ] [-2^{31}, 2^{31} - 1] [231,2311],所以我们可能会出现精度损失的情况。

首先,还是先把符号位s,阶码exp,尾数frac都抠出来。

我们计算一下真实的阶数, E = e x p − 127 E = exp - 127 E=exp127

如果fNaN,或者 E > 31 E > 31 E>31,根据题意,我们应该直接返回0x80000000u

如果 E < 0 E < 0 E<0,代表f只有小数部分,我们直接返回0即可。

我们算出真实的尾数 M = f r a c ∣ ( 1 < < 23 ) M = frac | (1 << 23) M=frac(1<<23)

如果 E > 23 E > 23 E>23我们就需要在后面补 E − 23 E - 23 E23个0,否则,我们需要舍去 23 − E 23 - E 23E位。

最后,搭配上符号位输出即可。

/* 
 * floatFloat2Int - Return bit-level equivalent of expression (int) f
 *   for floating point argument f.
 *   Argument is passed as unsigned int, but
 *   it is to be interpreted as the bit-level representation of a
 *   single-precision floating point value.
 *   Anything out of range (including NaN and infinity) should return
 *   0x80000000u.
 *   Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while
 *   Max ops: 30
 *   Rating: 4
 */
int floatFloat2Int(unsigned uf) {
   unsigned s = uf & (1 << 31);
   int exp = (uf & 0x7f800000) >> 23;
   unsigned frac = uf & (~0xff800000);

   int E = exp - 127;
   if(exp == 255 || E > 31) return 0x80000000u;
   if(E < 0) return 0;
   unsigned M = frac | (1 << 23);
   int V = (E > 23 ? M << (E - 23) : M >> (23 - E));
   if(s) V *= -1;
   return V;
}
13. floatPower2

这个函数让我们算出 2 x 2 ^ x 2x的单精度浮点数表示并返回。

x只能位于 [ − 149 , 128 ] [-149, 128] [149,128]之间。

根据不同情况输出即可,要考虑到尾数也可以额外表示浮点数。文章来源地址https://www.toymoban.com/news/detail-722054.html

/* 
 * floatPower2 - Return bit-level equivalent of the expression 2.0^x
 *   (2.0 raised to the power x) for any 32-bit integer x.
 *
 *   The unsigned value that is returned should have the identical bit
 *   representation as the single-precision floating-point number 2.0^x.
 *   If the result is too small to be represented as a denorm, return
 *   0. If too large, return +INF.
 * 
 *   Legal ops: Any integer/unsigned operations incl. ||, &&. Also if, while 
 *   Max ops: 30 
 *   Rating: 4
 */
unsigned floatPower2(int x) {
   if(x >= 128) return 0x7f800000;
   if(x >= -126) return (x + 127) << 23;
   if(x >= -149) return 1 << (x + 149);
   return 0;
}

到了这里,关于CSAPP实验一:DataLab的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 操作系统实验——银行家算法

    掌握银行家算法思想,并能编程实现。 1、在Linux环境下编译运行程序; 2、按照教材的算法编写; 3、(*)输入数据从文本文件中读出,不从键盘录入,数据文件格式见以下说明; 4、主要数据结构的变量名和教材中的一致,包括Available、Max、Allocation、Need、Request、Work、Fin

    2024年02月01日
    浏览(39)
  • 磁盘调度算法(操作系统实验 C++)

    通过这次实验,加深对磁盘调度算法的理解,进一步掌握先来先服务FCFS、最短寻道时间优先SSTF、SCAN和循环SCAN算法的实现方法。 问题描述: 设计程序模拟先来先服务FCFS、最短寻道时间优先SSTF、SCAN和循环SCAN算法的工作过程。假设有n个磁道号所组成的磁道访问序列,给定开

    2024年02月07日
    浏览(56)
  • 操作系统实验——进程管理的算法实现

    笔者在大学下属的事业单位上班,最近去帮着带下操作系统的实验课,这里随手水点参考代码,欢迎各位领导老师莅临指正 编写一个简单的进程调度器 进程控制块(PCB)的定义与管理 进程调度算法的实现 进程创建、销毁和切换 给定一批进程对比3-4种调度算法的时间(自选

    2024年02月06日
    浏览(46)
  • 操作系统实验—进程调度算法(java)

    目录 文章目录 前言 一、实验原理 二、实验步骤 1.创建PCB类 2.创建创建类 3.设计主窗口类 4.调度界面函数 5.算法类及其调度算法通用函数 6.进程调度算法函数 总结 操作系统实验1:进程调度算法,步骤3、4在一个类中,步骤5、6在一个类中。 (1)先到先服务调度算法:按照进程提

    2024年02月04日
    浏览(49)
  • 操作系统实验 银行家算法C++

    实验目的: 编程实现安全性算法及银行家算法,以帮助深刻理解银行家算法避免死锁的原理。 算法流程图:     实现代码:    验证数据: 运行结果:     说明: 本文章是在原作者的银行家算法文章基础上依据实验课要求修改和完善的,仅供参考,侵权删。  原作者地址

    2024年02月05日
    浏览(45)
  • 操作系统:实验一:进程调度实验——最高优先数优先的调度算法以及先来先服务算法 源码

    一、实验目的 (1)了解进程实体PCB结构; (2)理解进程不同状态和状态之间的转换过程; (3)掌握优先数的调度算法和先来先服务算法; 二、实验内容与要求 设计一个有 N个进程共行的进程调度程序 四、实验步骤 (1)实验设计 进程调度算法: 采用最高优先数优先的调

    2024年02月06日
    浏览(45)
  • 【操作系统原理实验】银行家算法模拟实现

    选择一种高级语言如C/C++等,编写一个银行家算法的模拟实现程序。1) 设计相关数据结构;2) 实现系统资源状态查看、资源请求的输入等模块;3) 实现资源的预分配及确认或回滚程序;4) 实现系统状态安全检查程序;5) 组装各模块成一个完整的模拟系统。 (1)设计思想: 1、

    2024年02月01日
    浏览(44)
  • 计算机操作系统实验-进程调度模拟算法

    进程调度是处理机管理的核心内容。本实验要求用高级语言编写模拟进程调度程序,以 便加深理解有关进程控制快、进程队列等概念,并体会和了解优先数算法和时间片轮转算法 的具体实施办法。 1.设计进程控制块 PCB 的结构,通常应包括如下信息: 进程名、进程优先数(

    2024年02月05日
    浏览(74)
  • 计算机操作系统实验:页面置换算法的实现

    本实验的目的是通过编程模拟不同的页面置换算法,比较它们的缺页率和命中率,加深对操作系统内存管理的理解。本实验采用C语言编写,实现了最佳置换算法(OPT)、先进先出置换算法(FIFO)和最近最久未使用算法(LRU)。实验中,页面号引用串从文本文件中读取,输出

    2024年02月02日
    浏览(39)
  • 操作系统实验:页面置换算法——FIFO、LRU 代码实现

            最简单的页面置换算法是FIFO。 在分配内存页面数( AP )小于进程页面数( PP )时,最先运行的 AP个页面放入内存;当内存分配页面被占满时,如果 又需要处理新的页面,则将原来放的内存中的AP个页中 最先进入 的调出(FIFO),再将新页面放入;所使用的内存

    2024年02月08日
    浏览(34)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包