目录
前言
《深入理解计算机系统》(CSAPP) 官网附带的DataLab,这是我完成的第一个lab,大部分是自己独立思考完成,也有一部分参考了网上的答案,记录一下。
测试脚本
还有,从CSAPP官网上下载下来的测试程序使用起来比较复杂,我写了个linux脚本,大家有需要的可以参考
#!/bin/bash
make clean
make btest
./btest bits.c # 测试程序
./dlc bits.c # 语法检查
正文
INTEGER - 整数部分
bitXor
使用 ~ 和 & 实现 ^
画个真值表就明白了
/*
* 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)));
}
tmin
返回二进制补码下的最小整数
/*
* tmin - return minimum two's complement integer
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 4
* Rating: 1
*/
int tmin(void) {
return 1 << 31;
}
isTmax
如果x是二进制补码下的最大整数则返回1,否则返回0
简单判断下x和Tmax是否相等即可
/*
* 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 !(~(1 << 31) ^ x);
}
allOddBits
二进制下如果所有偶数位都是1则返回1,否则返回0
/*
* 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 m = 0xAA + (0xAA << 8);
int mask = m + (m << 16);
return !((x & mask) ^ mask);
}
negate
输入x,返回-x
/*
* negate - return -x
* Example: negate(1) = -1.
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 5
* Rating: 2
*/
int negate(int x) {
return ~x + 1;
}
isAsciiDigit
当 0x30 <= x <= 0x39 时返回1,否则返回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) {
// (x - 0x30 >= 0) && (0x39 - x >= 0)
int tmin = 1 << 31;
return (!((x + (~0x30 + 1)) & tmin)) & (!((0x39 + (~x + 1)) & tmin));
}
conditional
返回 x ? y : 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) {
x = ~(!x) + 1; // (x == 0 ? 0xffffffff : 0x00000000)
return (y & ~x) | (z & x);
}
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
*/
int isLessOrEqual(int x, int y) {
int signx = (x >> 31) & 1; // 算术右移, 所以要 &1
int signy = (y >> 31) & 1;
int signy_x = ((y + ~x + 1) >> 31) & 1;
return (signx & !signy) | (!(signx ^ signy) & !signy_x);
}
logicalNeg
用除了 ! 以外的运算符实现 ! 运算符
/*
* 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;
}
howManyBits
输入x,返回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 = (~sign & x) | (sign & ~x); // (x >= 0) ? x : ~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;
}
FLOAT - 浮点数部分
floatScale2
输入uf
若uf为INF或者NAN,返回uf;
否则将 uf × 2
若 uf × 2 >= INF,则返回INF
否则返回 uf × 2
/*
* 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) {
int sign = uf & 0x80000000; // 取出符号
int e = uf & 0x7f800000; // 取出指数
int m = uf & 0x007fffff; // 取出小数
if (e == 0)
return (uf << 1) | sign; // 非规格化的
if (e == 0x7f800000)
return uf; // INF || NAN
e += 0x00800000;
if (e == 0x7f800000)
return 0x7f800000 | sign; // 返回 INF
return sign | e | m; // 拼回去
}
floatFloat2Int
输入uf,返回 (int) uf
若出现上溢(包括NAN和INF),则返回0x80000000u
C语言编译器对于左移运算的一个优化卡了我好久
左移里一个比较特殊的情况是当左移的位数超过该数值类型的最大位数时,编译器会用左移的位数去模类型的最大位数,然后按余数进行移位
/*
* 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) {
int sign = uf & 0x80000000; // 取出符号
int e = uf & 0x7f800000; // 取出指数
int m = (uf & 0x007fffff) | 0x00800000; // 取出小数并加上整数位的1
if (e == 0)
return 0; // 非规格化的
if (e == 0x7f800000)
return 0x80000000u; // INF || NAN
e = (e >> 23) - 127; // 取出真实指数
// 左移里一个比较特殊的情况是当左移的位数超过该数值类型的最大位数时,编译器会用左移的位数去模类型的最大位数,然后按余数进行移位
// 故对于e > 31的情况必须要额外判断, 过大的左移位数并不会想当然的使e全部为0
if (e > 31)
return 0x80000000u;
if (e < 0)
return 0;
// m 如果不做任何处理, 相当于一个1.m的值左移了23位
if (e > 23)
m <<= e - 23;
else
m >>= 23 - e;
return sign ? (~m + 1) : m;
}
floatPower2
输入x,返回 2^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.
*
* 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) {
x += 127;
if (x <= 0)
return 0;
// 这题测试样例有点过多了, 会导致测试程序超时
// 将btest.c中的 #define TIMEOUT_LIMIT 10 修改为20, 即可解决
else if (x >= 255)
return 0x7f800000; // return INF;
return x << 23;
}