前言

出于记录📝和复习的需要,整理了下本人在做CSAPP深入理解计算机系统这本书的配套实验Data Lab的内容 [Updated 12/16/19] (README, Writeup, Release Notes, Self-Study Handout)

关于CSAPP配套实验更多内容或配套文件等,请访问CSAPP_Lab

介绍

这个Lab主要涉及了位运算,补码和浮点数等内容。完成Lab不仅要实现函数的功能,还要求仅用规定的操作符,操作符数目也在限定范围内,详细可以看我部分翻译的README文件.
由于题目限定在32位系统中,本人系统为ARM架构,故测试程序在本人docker x86_64的linux环境中运行.

通过make clean && make && ./btest命令可以看到题目的分数.

image-20220701234907932

题目

部分汉化:点我下载bits.c题目文件

  • bitXor(x,y) 只使用 ~ 和 & 实现 ^
  • tmin() 返回最小补码
  • isTmax(x) 判断是否是补码最大值
  • allOddBits(x) 判断补码所有奇数位是否都是1
  • negate(x) 不使用负号 - 实现 -x
  • isAsciiDigit(x) 判断 x 是否是 ASCII 码
  • conditional(x, y, z) 类似于 C 语言中的 x?y:z
  • isLessOrEqual(x,y) x<=y
  • logicalNeg(x) 计算 !x (不用 ! 运算符)
  • howManyBits(x) 计算表达 x 所需的最少位数
  • floatScale2(uf) 计算 2.0*uf
  • floatFloat2Int(uf) 计算 (int) f
  • floatPower2(x) 计算 2.0的x次方

bitXor(x, y)

题目说明:使用按位与&和按位取反~实现异或运算^。

异或运算的定义如下:

a⊕b=(¬a∧b)∨(a∧¬b)a⊕b=(¬a∧b)∨(a∧¬b)
根据分配律和德摩根定律,可以推出:

a⊕b =(¬a∧b)∨(a∧¬b)=(¬a∨(a∧¬b))∧(b∨(a∧¬b))
=(¬a∨¬b)∧(a∨b)=¬(¬a∧¬b)∧¬(a∧b)a⊕b
=(¬a∧b)∨(a∧¬b)=(¬a∨(a∧¬b))∧(b∨(a∧¬b))
=(¬a∨¬b)∧(a∨b)=¬(¬a∧¬b)∧¬(a∧b)

1
2
3
4
5
6
7
8
9
10
11
12
13
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);
}
/*根据布尔代数,可以通过 ~ 和 & ,即非和与操作实现异或操作。所谓异或就是当参与运算的两个二进制数不同时结果才为1
* 其他情况为0。C 语言中的位操作对基本类型变量进行运算就是对类型中的每一位进行位操作
* 所以结果可以使用“非”和“与”计算不是同时为0情况和不是同时为1的情况进行位与,即~(~x&~y)&~(x&y)*/

tmin

1
2
3
4
5
6
7
8
9
10
11
/* 返回最小的二进制补码整数

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

isTmax(x)

1
2
3
4
5
6
7
8
9
10
11
12
13
//2
/* 如果是补码最大值返回1
* 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) {
return !(((x ^ (x + 0x1)) + 0x1) | (!(~x)));
// 只有当x为-1即0xFFFFFFFF时,~x为0,此时(x ^ (x + 0x1)) + 0x1为0,与!(~x)相与得1,再用逻辑非运算符得到1。
// 当x为0x7FFFFFFF时,~x为1,(x ^ (x + 0x1)) + 0x1为0。
}

allOddBits(x)

1
2
3
4
5
6
7
8
9
10
11
12
/* 如果所有奇数位为1则返回1
* 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) {
return !((0xAAAAAAAA & x) ^ 0xAAAAAAAA);
// 0xAAAAAAAA的奇数位为1
}

negate(x)

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

isAsciiDigit(x)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//3
/* 如果0x30 <= x <= 0x39(字符'0'到'9'的ASCII码)则返回1
* 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) {
return !((x ^ 0x30) >> 3) | !((x ^ 0x39) >> 1);
// 0x30 : 110000
// 0x39 : 111001
}

conditional(x, y, z)

1
2
3
4
5
6
7
8
9
10
11
/* 实现三目运算符
* 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) {
x = ~(!!x) + 1;
return (x & y) | (~x & z);
}

isLessOrEqual(x, y)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* 如果x <= y返回1
* 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 sign = (y + ~x + 1) >> 31; // 判断y-x符号,在x,y符号都相同的情况下,如果y-x符号为负即结果为1,那么返回0
int bitXor = (x >> 31) ^ (y >> 31); // 判断x和y符号是否相同,相同则为0
return !!(((!sign) & (!bitXor)) | (bitXor & (x >> 31)));
// 在符号相同的情况下,只需关注y-x的符号不为负即可。
// 在符号不相同的情况下,只需关注x为负返回1的情况。
}

logicalNeg(x)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//4
/* 使用使用位级运算实现逻辑非 !
* 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;
// 如果x为非0数,那么(x|(~x+1))为0x80000000,右移31位为0xFFFFFFFF,加上0x1,为0
// 如果x为0,那么0+0x1就是1
}

howManyBits(x)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/* 返回用补码表示x所需的最小比特数
* 即一个数用补码表示最少需要几位?
* 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的符号

x = (sign & ~x) | (~sign & x); // 若为负数,则按位取反;若为非负数,则不变

b16 = !!(x >> 16) << 4;// 先看高16位是否含有1,若有则表示至少需要16位,所以给b16赋值为16(1 << 4 = 16)
x = x >> b16; // 若有1,则原数右移16位,因为上面已经确定是否至少需要16位(针对0~15);若没有1,则b16为0,x不用移位,继续往下面判断

b8 = !!(x >> 8) << 3; // 看剩余位的高8位是否含有1,若有则表示至少还需要8位,给b8赋值为8
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 + 0x1; // 最后加上符号位
}

floatScale2(x)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//float
/* 返回 2 * 浮点数
* floatScale2 - Return bit-level equivalent of expression 2*f for
* floating point argument f.
* 参数和结果都是以无符号int的形式传递,但它们应被解释为单精度浮点值的位级表示.
* 当参数为NaN时,返回参数
* Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while
* Max ops: 30
* Rating: 4
*/
unsigned floatScale2(unsigned uf) {
int exp = (uf & 0x7F800000) >> 23; // 取出阶码
int sign = uf & 0x80000000; // 取符号位
if (exp == 0)
return uf << 1 | sign; // 若为非规格数,直接给uf乘以2后加上符号位即可
if (exp == 255)
return uf; // 若为无穷大或者NaN,直接返回自身
exp = exp + 1; // 若uf乘以2(也就是阶码加1)后变成255,则返回无穷大
if (exp == 255)
return (0x7F800000 | sign);
return (exp << 23) | (uf & 0x807FFFFF); // 返回阶码加1后的原符号数
// 浮点数有几种特殊情况:
// 1.若exp部分全为0(exp = 0),则是非规格化数,它是一种非常接近0的数;
// 2.若exp部分全为1(exp = 255),当小数部分全为0时,表示无穷大;当小数部分不为全0时,表示未初始化数据NaN;
// 3.以上两种情况以外,就是规格化数。
// 所以我们需要判断uf是哪一种浮点数,并根据它的类型来进行相应的操作
}

floatFloat2Int(x)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/* 将浮点数转换为整数
* floatFloat2Int - Return bit-level equivalent of expression (int) f
* for floating point argument f.
* 参数以无符号int形式传递,但它应被解释为单精度浮点值的位级表示.
* 任何超出范围的东西(包括NaN和无穷大)都应该返回0x80000000u.
* Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while
* Max ops: 30
* Rating: 4
*/
int floatFloat2Int(unsigned uf) {
int exp = ((uf & 0x7F800000) >> 23) - 127; // 计算出指数E
int sign = uf & 0x80000000; // 取符号位S
int frac = ((uf & 0x007FFFFF) | 0x00800000); // 如果为规格化的值,尾数M = 1 + 小数位f

if (exp > 30) // 因为int类型最大值2^31,2^31乘以大于1的数必然大于2^31.
return 0x80000000; // 若原浮点数指数大于31,返回溢出值
if (exp < 0)
return 0; // 若浮点数小于0,则返回0
if (exp > 8)
frac = frac << 8 >> 23; // 如果exp > 8,<< 超过8的话会丢失数据.
else
frac = frac << exp >> 23; // 将小数转化为整数,<< exp 等于 乘以2^exp ,然后再右移23位.

if (!sign)
return frac; // 正数
else
return ~frac + 1; // 负数
}

floatPower2(x)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/* 返回2.0^x
* floatPower2 - Return bit-level equivalent of the expression 2.0^x
* (2.0 raised to the power x) for any 32-bit integer x.
*
* 返回的无符号值应该具有与单精度浮点数2.0^x相同的位表示。
* 如果结果太小,不能以正负值表示,则返回 0. 如果太大,则返回+INF.
*
* Legal ops: Any integer/unsigned operations incl. ||, &&. Also if, while
* Max ops: 30
* Rating: 4
*/
unsigned floatPower2(int x) {
int INF = 0xFF << 23; // 设定一个最大值,也就是阶码位置都为1 即 0x7F800000
int exp = x + 127; // 计算阶码e
if (exp <= 0)
return 0; // 阶码小于等于0,则返回0
if (exp >= 255)
return INF; // 阶码大于等于255,则返回INF
return exp << 23; // exp向左移,回到标准浮点数的位置
}