Environment Construction
确保有一个linux系统,并已经执行过以下两条命令:
安装gcc:sudo apt-get install build-essential
安装gcc的交叉编译环境.):sudo apt-get install gcc-multilib
,因为实验的程序需要以32位方式编译
在CMU的CSAPP网站上下载实验所需资料,包括**README, Writeup,Self-Study Handout,**这三部分均包含对实验的要求说明(Handout的说明在其包含的bits.c文件中由注释给出),Self-Study Handout包括用于测试的文件
Lab1: DataLab
1.bitXor(x,y)
要用~和&实现异或^,即将结果中 1-0,0-1对应的位设置为1
x&y中为1的位(bit)对应 1-1; 取反后为:0-0、0-1、1-0;
(x&y)为1的位(bit)对应 0-0; 取反后为:1-1、0-1、1-0;
两个做交集即为结果。(位向量可以表示集合,&,|,~可视为 交,并,补操作)
/*
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) ; // if regardless '+' is illegal:(~x&y) + ((x)&(~y)) or ~((x&y) + ((~x)&(~y)))
}
2.tmin
最简单的一题:000...001
--> 1000...000
/*
tmin - return minimum two's complement integer
Legal ops: ! ~ & ^ | + << >>
Max ops: 4
Rating: 1
*/
int tmin(void) {
return 1<<31;
}
3.isTmax(x)
这题最开始想到 Tmin的一个性质,即对二进制补码 Tmax关于加法的逆为其本身:Tmax+Tmax = 0;因此利用这个性质写出了!((~x) + (~x))
,但测试结果出乎意料,加法溢出导致了未知的行为。
根据 Tmax +1 = Tmin 的性质可以得出 , 100...000
+ 011...111
= 111..1111
(-1),可得出!(~x^(x+1))
(^可替换为+)
处理特例-1: -1同样会产生结果1,根据 -1+1==0
,Tmax+1!=0
,进而!(-1+1) !=0
,!(Tmax+1) ==0
.
所以对Tmax, x+(x+1) = x
, 对-1,x+(x+1)!=x
用x+(x+1)
替换原式中的第一项x,最终得出结果:!(~((x+!(x+1))^(x+1)))
/*
isTmax - returns 1 if x is the maximum, two's complement number,
and 0 otherwise
egal ops: ! ~ & ^ | +
Max ops: 10
Rating: 1
*/
int isTmax(int x) {
return !(~((x+!(x+1)) ^ (x+1))) ;
// !((~x) + (~x)); it should be right, the operator "!" seem to not work
}
4.allOddBits(x)
这道题没想出来,在x上shift的方式想了一个多小时,总是不能满足所有测试用例,说明在x上shift是行不通的。
用好异或即可解决:构造101...1010
,再用该数提取x中的奇数位,最后再与101...1010
比较
/*
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 allOdd = (0xAA << 24) + (0xAA << 16) + (0xAA << 8) + 0xAA; // 10101010..101
return ! ((allOdd & x) ^ allOdd);
}
5.isAsciiDigit(x)
有点难,还是自己做出来了,主要使用了掩码提取x中的指定位,再运用前几题的经验—用异或执行比较操作。
x的最后四位,3bit 与 1,2bit不能同时为1,因而有!((x&mask2)^mask2) + (!((x&mask3)^mask3)))
,难点在于怎么处理好式中三部分的逻辑关系
/*
* 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 mask1 = 0x3; // 000...0011
int mask2 = 0xA; // 1010
int mask3 = 0xC; // 1100
return !( ((x>>4)^mask1) | (!((x&mask2)^mask2) + (!((x&mask3)^mask3)) ) );
}
6.conditional
比较简单,主要实现这样一个逻辑:x!=0,返回y;x=0,返回z;
涉及的操作是把x转化为0与1两个值,再把000...0001
转化为111...1111
/*
* 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 judge = !(x ^ 0x0); // x=0 -> judge=1,whereas x!=0 -> judge=0
judge = (judge << 31)>>31; // 000...000 or 111...111
return ((~judge)&y) | (judge&z);
}
7.isLessOrEqual(x, y)
可通过减法y-x>=0
判断x<=y
,由于不存在-符,所以取x关于加法的逆-x,进而变为 x+y
那么这题就涉及加法溢出,需要对x+uw y
结果的三种情况的判断(negative overflow , positive overflow),变得复杂起来。
更好的想法是分析式子**y-x**
并加入一个conditional操作:如果两者异号(正-负,负-正),那么结果的正负的确定的;如果两者同号(同号相减不可能溢出),则通过与Tmin相与提取符号位。
/*
* 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 Tmin = 1<<31; // 100...0000
int signY = Tmin & y;
int signX = Tmin & x;
int judge = (signY ^ signX)<<31;
x = (~x)+1;
return (judge&signX) | (~(judge>>31) & !((y+x)&Tmin)) ; //
}
8.logicalNeg(x)
这题要求自己实现一个 !逻辑,即输入0返回1,输入N(N!=0)返回0。一开始的出发点是:x=0,返回1;x 位向量存在为1的位,返回0。但是仅靠逻辑运算符无法实现该想法。
于是换了一个想法:先得到x的符号位signX。signx为1,说明x为负数,可以直接得到结果;sign为0,说明x即可能为0也可能为正数,那么就要利用补码加法操作会发生的positive overflow现象,即 Tmax + x ,对任意x>0均会使结果变为负数,符号位由0 -->1。(positive overflow 不同于 negative overflow,并没有产生整数溢出,因此不会导致undefined behavior)
/*
* logicalNeg - implement the ! operator, using all of
* the legal operators except !
* Examples: logicalNeg(3) = 0, logi'calNeg(0) = 1
* Legal ops: ~ & ^ | + << >>
* Max ops: 12
* Rating: 4
*/
int logicalNeg(int x) {
int Tmin = 1<<31;
int Tmax = ~Tmin;
int signX = ((x&Tmin)>>31) & 0x1;
return (signX^0x1) & ((((x + Tmax)>>31)&0x1)^0x1);
}
9.howManyBits(x)
这题一开始想的是去除符号位后,找位向量中最左边的1的位置序号,但是我忽略了补码的一个性质:当数的符号位为1时,将数按符号位扩展之后其值不会变,如1101与101表示的是同一个值(-3),因此找到最左边的1并不能得到最短的位数。
要找到能表示负数的最短位数,而又不受符号位拓展的影响,便要找最左边的0,而不是1。为与对正数的操作相统一,做法是把负数按位取反(Such as: 1101 -> 0010)
按二分法逐步缩小范围,找到最左边的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 signX = x>>31;
x = ((~signX) & x) | (signX&(~x));// if x is negative, let sign bit:1-> 0
b16 = (!!(x>>16))<<4; // ensure high 16 bits exist 1 or not
x=x>>b16;
b8 = (!!(x>>8))<<3; // ensure high 8 bits
x=x>>b8;
b4 = (!!(x>>4))<<2; // ensure high 4 bits
x=x>>b4;
b2 = (!!(x>>2))<<1; // ensure high 2 bits
x=x>>b2;
b1 = !!(x>>1); // ensure 31 bits or not
x = x>>b1;
b0 = x;
return b0+b1+b2+b4+b8+b16+1; // 1: sign bit
}
10.floatScale2(uf)
先对题目做出一点解释:传入一个unsigned
类型的参数,但是函数内将它解释为一个浮点数类型,即参数的值不是参数的十进制值,而是其二进制形式表示的浮点数值(M×2E)
整体思路:用掩码分别提取sign,exponent,fraction三部分,再根据exp的值分类讨论
注意点:对normalized,f*2的2是乘在了2E;而对denormalized,是乘在了frac表示的M上,这也是为什么frac = frac <<1
,这也使得denormalized能转化到normalized (smoothly)
//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 // revision: NaN or infinity
* Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while
* Max ops: 30
* Rating: 4
*/
unsigned floatScale2(unsigned uf) {
int musk_exp,musk_frac,sign,exp,frac,result;
musk_exp = 0xFF << 23;
musk_frac = 0x7FFFFF;
exp = (uf & musk_exp)>>23;
frac = uf & musk_frac;
sign = 0x1<<31 & uf;
result = 5;
if(exp == 0xFF ) // NaN
result = uf;
else if(exp == 0x0) // denormalized
{
if(frac == 0x0)
{
if(sign) // -0.0
result = uf;
else // +0.0
result = 0 ;
}
else
{
frac = frac << 1;
result = sign+ (exp<<23) + frac;
}
}
else if(exp != 0x0 && exp != 0xFF) // normalized
{
exp += 1;
result = sign+ (exp<<23) + frac;
}
return result;
}
11.floatFloat2Int(uf)
浮点数类型的这几题比前面的题要轻松很多,大概是因为可用符号和结构比较充足的原因吧。
对题目的解释:返回浮点数f的int型表示,如输入12345.0 (0x4640E400)
, 正确输出为12345 (0x3039)
注意点:当f的值超过32bit的int类型位向量所能表示的最大值时(2^31-1),即E>31时,属于out of range
/*
* 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 musk_exp,musk_frac,exp,frac,sign,E,Bias,result;
musk_exp = 0xFF << 23;
musk_frac = 0x7FFFFF;
exp = (uf & musk_exp)>>23;
frac = uf & musk_frac;
sign = 0x1<<31 & uf;
Bias = 127;
result = 5;
if(exp == 0xFF ) // NaN or infinity
result = 0x80000000u;
else if(exp == 0x0)
result = 0;
else if(exp != 0x0 && exp != 0xFF) // normalized
{
E = exp -Bias; // bit_num of fraction
if(E < 0)
result = 0;
else if (E>31)
result = 0x80000000u;
else
{
frac = frac>>(23-E);
result = (0x1 << E) + frac ;
if(sign == 0x1<<31)
result = - result;
}
}
return result;
}
12.floatPower2(x)
注意点:当2^x超过位向量所能表示的最大值(largest normalized)时,即exp 大于 254(1111 1110),属于too large
/*
* 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) {
int exp,frac,E,Bias,result;
Bias = 127;
result = 5;
E = x;
if(x<1 && x!=0)
return 0;
else if(x >= 0x1 || x == 0)
{
frac = 0x0;
exp = E+Bias;
if(exp > 254) // 1111 1110
{
exp = 0xFF;
result = exp <<23+frac;
}
else
result = (exp<<23) + frac;
}
return result ;
}
Consequence
make
./driver.pl