【CSAPP】Data Lab实验笔记
前天讲到要刚CSAPP,这一刚就是两天半.CSAPP果然够爽,自带完整的说明文档,评判程序,辅助程序.样例直接百万组走起,管饱!
datalab讲的是整数和浮点数怎么用二进制表示的,考验的是用基本只用位运算来实现一些看似稀松平常的操作.实际体会就是看题五秒钟,脱发两小时.当然做出来的成就感也是爆棚的.
求异或
根据离散数学等值演算写出公式
//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的数就是了
/*
* tmin - return minimum two's complement integer
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 4
* Rating: 1
*/
int tmin(void) {
return (1<<31);
}
求补码可表示的最大数
这题开始需要一个技巧:!!x可以把非零数变成1,把0变成0
tmax=0x7FFFFFFF
tmax+1=0x10000000
(tmax+1)+(tmax+1)=0x00000000
C语言形式写作: x=tmin()-1
变形得 x+1=tmin()
a ^ b仅在每一位都相同时为假,则可取反用!(a ^ b)表示仅在每一位都相同时为1
最后额外排除0xFFFFFFFF
//2
/*
* 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+1)+(x+1))&!!(x+1);
}
判断所有偶数位是否均为1
令y=1010...1010(二进制),即y==0xAAAAAAAA
运用<<和+来让0XAA重复四次得到该值
当且仅当x的任意偶数位都为1时返回1
即x存在偶数位为0时返回0
即结果为!(~x&0XAAAAAAAA)
/*
* 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 y=0xAA;
y=(y<<8)+y;
y=(y<<16)+y;
return !(~x&y);
}
求-x
根据公式直接写
/*
* negate - return -x
* Example: negate(1) = -1.
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 5
* Rating: 2
*/
int negate(int x) {
return (~x+1);
}
判断是否为'0'~'9'
!(a^b)在当且仅当a与b任意一位都相同时为1,可以代替==
查表得规律:
00...011100X表示38~39
00...0110XXX表示30~37
故借助于<<分别判断之
//3
/*
* 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>>1)^0x1C) | !((x>>3)^0x6);
}
实现x?y:z
假设存在操作@,
x=1时有x@y=y
x=0时有x@y=0
联想到&的性质可知
x=111...111(二进制)时x&y=y
x=000...000(二进制)时x&y=0
而111...111(二进制)=0XFFFFFFFF=-1
再根据前面求负数的题,~a+1=-a
/*
* 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) {
return ((~!!x+1)&y)|(~!x+1&z);
}
判断小于等于
直接求y-x在异号的时候会溢出
(x>>31)^(y>>31)判断xy符号位是否不同
sub=y-x=y+~x+1求差值
若同号,y-x<0时返回1
若异号,x<0时返回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 sx=x>>31;
int sy=y>>31;
int diff=sx^sy;
int sub=(y+~x+1)>>31;
return ((!diff&!sub)|(diff&sx))&1;
/*
另一种方法:分x>=0?,y>=0?四种情况讨论
int xb=x>>31;
int xa=~xb;
int yb=y>>31;
int ya=~yb;
return (xa&((ya&!((y+~x+1)>>31))))|(xb&((ya&1)|(yb&!!((~y+x)>>31))));
*/
}
另一种思路:分x>=0?,y>=0?四种情况讨论
int isLessOrEqual(int x, int y) {
int xb=x>>31;
int xa=~xb;
int yb=y>>31;
int ya=~yb;
return (xa&((ya&!((y+~x+1)>>31))))|(xb&((ya&1)|(yb&!!((~y+x)>>31))));
}
实现!运算符
关键知识点:当且仅当x==0时,x与-x符号位相同,并且值为0
故判断~(-x|x)的符号位即可
//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+1)|x)>>31)&1;
}
另一种思路:我管这叫二分折叠
int logicalNeg(int x) {
int a=(x>>16)|x;
int b=(a>>8)|a;
int c=(b>>4)|b;
int d=(c>>2)|c;
int e=(d>>1)|d;
return (~e)&1;
}
判断位数
看Max ops知难度.
预处理:
把负数转换成它的~,使得正负数可用同一套操作
每个数都有一个符号位,所以计数器初始值为1
之后二分法:
判断高十六位是否为0,是的话把计数器加16,然后截取高十六位作为新的x;
否则节取低十六位作为新的x
判断高八位是否为0,是的话把计数器加8,然后截取高八位作为新的x;
否则节取低八位作为新的x
......
这个不断判断的过程可以借用前面x?y:z的代码,最后做一点点化简,防止超90ops
/* 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 ret=0;
int h16,h8,h4,h2,h1;
int sign=(x>>31)&1;
x=((~!!sign+1)&(~x))|(~!sign+1&x);
ret=1;
h16=(x>>16);
ret+=((~!!h16+1)&16);
x=h16|(~!h16+1&((x<<16)>>16));
h8=x>>8;
ret+=((~!!h8+1)&8);
x=h8|(~!h8+1&((x<<8)>>8));
h4=x>>4;
ret+=((~!!h4+1)&4);
x=h4|(~!h4+1&((x<<4)>>4));
h2=x>>2;
ret+=((~!!h2+1)&2);
x=h2|(~!h2+1&((x<<2)>>2));
h1=x>>1;
ret+=((~!!h1+1)&1);
ret+=!!x;
return ret;
进一步化简后:
int howManyBits(int x) {
int b16,b8,b4,b2,b1;
int ret=0;
int sign=(x>>31);
x=(~sign&x)|(sign&~x);
ret=1+!!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);
return b16+b8+b4+b2+b1+ret;
}
求浮点数f*2
if-else解封了还有啥难得?分类讨论,一个坑点在于无符号数右移的时候是无符号右移
//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 e=(uf<<1)>>24;
if(e==0xFF) return uf;
else if(e==0) return (uf<<1)+(uf&0x80000000);
else return uf+(1<<23);
}
float转int
注意了<<和>>都不能超过自身的范围,也不能用负数,否则可能发生不可预期的错误
/*
* 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 f=((uf<<11)>>11)+(1<<23);
unsigned e=(uf<<1)>>24;
int sign=(uf>>31)&1?-1:1;
int shift=e-127-23;
if(e==0xFF) return 0x80000000;
else if(e==0) return 0;
else{
if(shift<-23) return 0;
else if(shift>10) return 0x80000000;
else if(shift<0) return (f>>(-shift))*sign;
else return (f<<shift)*sign;
}
}
求2.0^x
这题我竟然超时了.原因是我是在移动硬盘上装得linux,而评判程序恰好时运行在本地的,这样子速度就慢了一截,结果就超时了.
/*
* 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<=-127) return 0;
if(x>=128) return 0x7f800000;
return (127+x)<<23;
}