【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;
}
posted @ 2020-05-31 23:41  康宇PL  阅读(771)  评论(3编辑  收藏  举报