[csapp]datalab作答记录

datalab作答记录

零、简要说明

  此为在课程学习中布置的datalab,相对于官网提供的版本是有所修改的,因此题目和官网的版本并不是一致的。但总体上来说大同小异,毕竟重要的不是题目,而是思想。
  这样的训练的主要目的是加深对系统的各种理解。因为感觉如果直接写完就扔掉不管的话,就会很快就忘记各种思路,使得训练达不到应有的效果,所以决定在CSDN上记录自己的思路,以加深印象,同时,如果可能的话,也希望能给后来人提供一些思路上的指引。

一、实验目的

  通过练习,熟练位操作级别的运算与二补数的性质。

二、函数分析

1、按位与的实现

函数实现:

/* NO: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));
}

函数分析:

  如图1-1所示,与门可以拆分为非门和或门的组合:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yNoULyIx-1606048416507)(C:\Users\蔡三圈\AppData\Roaming\Typora\typora-user-images\image-20201014192853830.png)]

图1-1

  而按位运算无非是分别对每一位进行运算,因此也可以像图1-1一样进行分解,分解为三个按位非和一个按位或

2、按位NOR的实现

函数实现:

/* NO:2
 * bitNor - ~(x|y) using only ~ and &
 *   Example: bitNor(0x6, 0x5) = 0xFFFFFFF8
 *   Legal ops: ~ &
 *   Max ops: 8
 *   Rating: 1
 */
int bitNor(int x, int y) {
  return ~~((~x) & (~y));
}

函数分析:

  如图2-1所示,或门可以拆分成非门和与门的组合:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wg3R9TmC-1606048416514)(C:\Users\蔡三圈\AppData\Roaming\Typora\typora-user-images\image-20201014193230802.png)]

图2-1

  与1一样,按位或可以分解为三个按位非和一个按位与

3、复制最低位的bit

函数实现:

/* NO:3
 * copyLSB - set all bits of result to least significant bit of x
 *   Example: copyLSB(5) = 0xFFFFFFFF, copyLSB(6) = 0x00000000
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 5
 *   Rating: 2
 */
int copyLSB(int x) {
  return x << 31 >> 31;
}

函数分析:

  int型变量的移位是算术移位,而算术右移的特性是:在空出的高位补上原有的最高位的值。因此,只要将最低位移动到最高位,再将其右移,移回到最低位即可

4、偶数bit为1的整数

函数实现:

/* NO:4
 * evenBits - return word with all even-numbered bits set to 1
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 8
 *   Rating: 2
 */
int evenBits(void) {
  int a = 0x55;
  return a + (a << 8) + (a << 16) + (a << 24);
}

函数分析:

  最低位为第零位,也是偶数,因此此数满足:01010101…0101的特性。由于每八位都是相等的,并且允许定义0x00-0xff范围内的常数,因此,定义一个常数a=0x55( 0101010 1 2 01010101_2 010101012),并分别将其左移8位、16位、24位,以完成拼接。

5、逻辑移位

函数实现:

/* NO:5
 * logicalShift - shift x to the right by n, using a logical shift
 *   Can assume that 1 <= n <= 31
 *   Examples: logicalShift(0x87654321,4) = 0x08765432
 *   Legal ops: ~ & ^ | + << >>
 *   Max ops: 16
 *   Rating: 3
 */
int logicalShift(int x, int n) {
  int firstStep = ((x >> 1) & ~(1 << 31));
  return firstStep >> (n + (~1 + 1));
}

函数分析:

  逻辑移位,关键在于高位补零。因此,如果要将算术移位换为逻辑移位,只需要保证最高位为0即可。
  在此处采用的方法是:先右移一位,强行将补上的最高位设置为0,随后再进行剩余次数的移动,这样可以保证此后的算术移位都是补0。
  注: ∼ 1 + 1 \sim 1+1 1+1表示 − 1 -1 1,因为此处不能使用减号,故使用此形式表示相减。

6、逻辑非的实现

函数实现:

/* NO:6
 * bang - Compute !x without using !
 *   Examples: bang(3) = 0, bang(0) = 1
 *   Legal ops: ~ & ^ | + << >>
 *   Max ops: 12
 *   Rating: 4
 */
int bang(int x) {
  int y = ~(~x + 1) & (~x);
  int z = y >> 31;
  return (~z) + 1;
}

函数分析:

  此函数的主要目的是将非零的数映射到0,将0映射到1。因此从0的独特之处开始思考:只有0和TMin具有 ∼ x + 1 = x \sim x+1=x x+1=x的特性,这个特性会非常地有用。
  分别列出 x = 0 x=0 x=0 x = T M i n x=TMin x=TMin以及 x x x为其他数时、 ∼ x + 1 \sim x+1 x+1 x x x的形式:

value x x x ∼ x + 1 \sim x+1 x+1
000…0000…00
TMin10…0010…00
others_pos0*…**1*…**
others_neg1*…**0*…**

  现在的目标是分离出0这个情形——让它与其他三个情形独立开来。最直接的方法就是右移:将所有位全部变为最高位。
  如果使用 x & ( ∼ x + 1 ) x\&(\sim x+1) x&(x+1),则会使得只有 x = T M i n x=TMin x=TMin对应的运算结果为11…11,其余的都是00…00。而如果两端取反,使用 ( ∼ x ) & ∼ ( ∼ x + 1 ) (\sim x)\&\sim(\sim x+1) (x)&(x+1),则会使得只有 x = 0 x=0 x=0对应的运算结果为11…11(对应十进制的-1),其余的都是00…00(对应十进制的0)。这正是想要的情形。
  而由于对0取逻辑非的结果为1,对非0取逻辑非的结果为0,则只需将上述对 x = 0 x=0 x=0运算得到的结果-1取负即可(对其他情形下的运算结果0,取负后仍然为0),最后返回目标值。

7、最小bit位

函数实现:

/* NO:7
 * leastBitPos - return a mask that marks the position of the
 *               least significant 1 bit. If x == 0, return 0
 *   Example: leastBitPos(96) = 0x20
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 6
 *   Rating: 4
 */
int leastBitPos(int x) {
  return x & ((~x) + 1);
}

函数分析:

  设x=…0010…00,其中1后面都是0。
  则~x=…1101…11,原来的1变成了0,后面全是1。
  因此,~x+1,即-x,等于…1110…00,注意到从原来的1那个位置往后数,还是和之前是一样的,而往前数,则全部反了过来。
  此函数的目的是要把1前面的全部数都置0,后面的保留为100…00。因此,使用 x & ( ∼ x + 1 ) x\&(\sim x+1) x&(x+1)即可达到目的:最靠后的1保留了下来,其他位全为0。

8、返回TMax

函数实现:

/* NO:8
 * TMax - return maximum two's complement integer
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 4
 *   Rating: 1
 */
int tmax(void) {
  return (1 << 31) + (~1) + 1;
}

函数分析:

  TMax= 0111...111 1 2 0111...1111_2 0111...11112,而TMax+1= 1000...000 0 2 1000...0000_2 1000...00002,因此,只要轻松地得到后者,再减去1就可以得到前者。

9、返回-x

函数实现:

/* NO: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 -x=\sim x+1 x=x+1即可。

10、x是否是正数

函数实现:

/* NO:10
 * isPositive - return 1 if x > 0, return 0 otherwise
 *   Example: isPositive(-1) = 0.
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 8
 *   Rating: 3
 */
int isPositive(int x) {
  int nonNeg = ((x >> 31) + 1);
  int zero = (((x + (~1) + 1) >> 31) + 1);
  return nonNeg & zero;
}

函数分析:

  整体分为两个部分。
  第一部分:输入的x是否非负。如果非负,则x>>31必然为0,即:置nonNeg=1。而如果为负,则x>>31必然为-1,即:置nonNeg=0.
  第二部分:输入的x与零的关系(此处命名有歧义)。因为要从第一部分中判定为1的集合里,将0抹去,所以此处应该满足:对于输入0,返回0,对于输入正数,返回1。容易注意到,在大于等于0的数中,只有0减去1后会变成负数,因此,对((x-1)>>31)+1进行判定即可(与上述判定类似,只不过这里的x变成了x-1)。
  最后,对第一部分( x ≥ 0 x\ge0 x0)和第二部分( x ≠ 0 x\neq 0 x=0)进行与运算即可。

11、x是否是非负数

函数实现:

/* NO:11
 * isNonNegative - return 1 if x >= 0, return 0 otherwise
 *   Example: isNonNegative(-1) = 0.  isNonNegative(0) = 1.
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 6
 *   Rating: 3
 */
int isNonNegative(int x) {
  return (x >> 31) + 1;
}

函数分析:

  此函数实现的功能完全与上个函数的第一部分一致,因此可以直接照搬。

12、使用一次加法,求三个数的和

函数实现:

/* NO:12
 * sum3 - x+y+z using only a single '+'
 *   Example: sum3(3, 4, 5) = 12
 *   Legal ops: ! ~ & ^ | << >>
 *   Max ops: 16
 *   Rating: 3
 */
/* A helper routine to perform the addition.  Don't change this code */
static int sum(int x, int y) {
  return x+y;
}
int sum3(int x, int y, int z) {
  int word1 = 0;
  int word2 = 0;
  /**************************************************************
     Fill in code below that computes values for word1 and word2
     without using any '+' operations
  ***************************************************************/
  word1 = x ^ y ^ z;
  word2 = ((x & y) | (y & z) | (z & x)) << 1;
  /**************************************************************
     Don't change anything below here
  ***************************************************************/
  return sum(word1,word2);
}

函数分析:

  首先,对于两个数的加法,a+b:
  在不考虑进位的情况下,a+b的每一位应该满足a^b,即:

^01
001
110

  现在考虑进位:如果a和b上的某一位都为1,则进一位,进位自然是给左边的那一位加1。由此描述,进位是由(a&b)<<1来表示的。即:a+b=a^b+(a&b)<<1
  而对于三个数的加法,a+b+c:
  不考虑进位时,容易看出类似于a+b地,a+b+c的每一位应该满足a^b^c。而对于进位,即便a、b、c上的某一位都为1,也只需要给左边进1。因此,不必考虑更复杂的情形,只要a、b、c中至少有两个都为1时,就可以产生进位,且两个为1和三个为1带来的进位都是一样的。
  综上所述:a+b+c=a^b^c+((a&b)|(b&c)|(c&a))<<1

13、判断是否发生溢出

函数实现:

/* NO:13
 * addOK - Determine if can compute x+y without overflow
 *   Example: addOK(0x80000000,0x80000000) = 0,
 *            addOK(0x80000000,0x70000000) = 1,
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 20
 *   Rating: 3
 */
int addOK(int x, int y) {
  int signIdentical = !((x >> 31) ^ (y >> 31));
  int signChange = !((((x + y) >> 31) ^ (x >> 31)) + 1);
  return !(signIdentical & signChange);
}

函数分析:

  int型整数加法溢出时,必然满足:
  1、两个加数同号,即符号位相同
  2、加法的和与加数异号,即符号位相反
  因此,首先使用异或,对两个加数的符号位进行了比较:让符号位填满全部32个bit,如果同号,则异或运算结果为0。反之,异号时,结果为-1。最后取逻辑非,使得同号结果为1,异号结果为0。
  随后使用了同样的方法,判断两数的和是否与加数异号,注意在最后取逻辑非之前,使用了+1,使得运算结果变成:同号为0,异号为1。
  对上述两个判定结果进行与运算,即可得到是否溢出的判断,最后根据函数的功能要求,再取一次逻辑非,使得溢出时返回0。

14、求绝对值

函数实现:

/* NO:14
 * logicalAbs - absolute value of x (except returns TMin for TMin)
 *   Example: abs(-1) = 1.
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 10
 *   Rating: 4
 */
int logicalAbs(int x) {
  int firstStep = (x ^ (x >> 31));
  int control = (~(x >> 31)) + 1;
  return firstStep + control;
}

函数分析:

  取绝对值时,正数应该保持不变,而负数则应该变成相应的正数。
  考虑使用异或的关系,让正数运算后不变,负数运算后被取反。发现负数右移31位的结果等于-1(11…11 2 _2 2),而正数右移31位的结果等于0(00…00 2 _2 2)。对于负数而言,移位得到结果在位级别上,全为1,与自身进行按位异或运算时,恰好能够将0变1,1变0。而对于正数而言,则是0和1保持不变。在这样的运算之后,就能够保证正数不变、负数取反。
  而负数取反后还要加上1才能真正等于绝对值(正数却不需要),因此要想办法构造一个“区别对待”的、要加上去的数,使得对正数的第一步运算后的结果+0,对负数的第一步运算后的结果+1。注意到正数的 ∼ ( x > > 31 ) + 1 \sim(x>>31)+1 (x>>31)+1结果为0,而负数对应的结果为1。这样一来,就找到了要加上去的数。
  因此,将上述两项加在一起返回即可。

15、判断非0

函数实现:

/* NO:15
 * isNonZero - Check whether x is nonzero using
 *              the legal operators except !
 *   Examples: isNonZero(3) = 1, isNonZero(0) = 0
 *   Legal ops: ~ & ^ | + << >>
 *   Max ops: 12
 *   Rating: 4
 */
int isNonZero(int x) {
  int y = ~(~x + 1) & (~x);
  int z = y >> 31;
  return z + 1;
}

函数分析:

  此函数的要求,实际上与第6个函数“逻辑非的实现”基本一致,都是区分0和非0的功能。
  只不过在此处,要把结果的0与1反过来,之前是将(-1,0)变成(1,0),那么现在只要将(-1,0)变成(0,1)就行了。因此,使用+1的手段即可。

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值