[关闭]
@SovietPower 2021-04-21T15:10:58.000000Z 字数 5288 阅读 1060

CSAPP Lab1

CSAPP



bits.c

在bits.c中做题目
使用make clean和make进行编译
调用./btest -f funcName测试funcName函数的结果,可以在代码中中插入printf输出中间结果,但是要记得最后删掉
调用./dlc bits.c查看是否使用了非法或过多的运算符
重复以上步骤,最后可以直接运行./btest输出最后结果。

得分图

本地的fitsBits的Check可能有问题,没法过。

补充一个位运算优先级图。

bitAnd

x&y using only ~ and |
~x~y中同时为的位即为x&y中为的位,或起来取反即可。

  1. int bitAnd(int x, int y) {
  2. return ~(~x|~y);
  3. }

getByte

Extract byte n from word x
0xFF分别与x,x>>8,x>>16,x>>24即可。

  1. int getByte(int x, int n) {
  2. return 0xFF&(x>>(n<<3));
  3. }

logicalShift

shift x to the right by n, using a logical shift
简单的想法是x>>n与一个高位为其余全的数取反就是,用就可以算术右移位得到高位的,然后再左移位即可。

令一个想法是,就是左移位。
有三个问题及前两个的解决方法依次是:
1. ,先左移位再右移位,最后再左移位,避免最高位的丢失。
2. 右移时是逻辑右移,要转成int或写成才是算术右移,才不丢失最高位的,但是不能有 类型转换 和 负号及十进制数。
所以必须是恰好左移位,解决方法是对二进制拆分(的二进制表示比更容易知道),写成个与和右移,最后再左移位。这样改完后代码:(x>>n)&~(0xFFFFFFFF<<(!(n&16)<<4)<<(!(n&8)<<3)<<(!(n&4)<<2)<<(!(n&2)<<1)<<(!(n&1))<<1);
3. 常数只能是(不过可以构造出来),以及运算符数超过了。所以这个方法不行。

  1. int logicalShift(int x, int n) {
  2. return (x>>n)&~(1<<31>>n<<1);
  3. }

bitCount

returns count of number of 1s in word
做法是整体的分治。令直到
先计算每两位中的个数,即为,每两位的的个数会存在这两位中。
再计算每四位中的个数,即,每四位的的个数会存在这四位中。
同理,每八位中的,十六位为,三十二位为,即为答案。

有个细节是,计算得到的每八位的个数不会超过,即不会溢出中限制的位,所以可以先加起来再一起减少一次操作数。同理。

还有限制常数最多为八位,所以需先构造,以及,以及

  1. int bitCount(int x) {
  2. int tv1=0x55|(0x55<<8),v1=tv1|(tv1<<16);
  3. int tv2=0x33|(0x33<<8),v2=tv2|(tv2<<16);
  4. int tv3=0xF|(0xF<<8),v3=tv3|(tv3<<16);
  5. int v4=0xFF|(0xFF<<16);
  6. int v5=0xFF|(0xFF<<8);
  7. int res=(x&v1)+(x>>1&v1);
  8. res=(res&v2)+(res>>2&v2);
  9. res=(res+(res>>4))&v3;
  10. res=(res+(res>>8))&v4;
  11. res=(res+(res>>16))&v5;
  12. return res;
  13. }

bang

Compute !x without using !
结果为当且仅当x=0,所以问题在于怎么将一个数的放到个位上。有个位的后,取反与就可以了。

类似bitCount的分治,用x|=x>>1求出两两分组后的两位中是否有并放到低位上,然后x|=x>>2,求出四个一组的数中是否有并放到低位上...直到x|=x>>16

x|=x>>16开始也可,先将汇聚到低位上,再x|=x>>8可以将汇聚到低位上,...直到x|=x>>1

还有种方法是,利用~x+1)的符号位均为,而其他数中有一个的性质,取出这个。即:~((x|(~x+1))>>31)&1

  1. int bang(int x) {
  2. return ~((x|(~x+1))>>31)&1;
  3. //Another sol:
  4. // x|=x>>1;
  5. // x|=x>>2;
  6. // x|=x>>4;
  7. // x|=x>>8;
  8. // x|=x>>16;
  9. // return ~x&1;
  10. }

tmin

return minimum two's complement integer
1<<31

  1. int tmin(void) {
  2. return 1<<31;
  3. }

fitsBits

return 1 if x can be represented as an n-bit, two's complement integer.
,则若为正数,则高位均为,若为负数,则高位均为。所以左移位后再右移位应不变。否则越界则数值会改变。
注意32-n=32+(~n+1)a==b可以改写为!(a^b)

  1. int fitsBits(int x, int n) {
  2. int delta=33+(~n);
  3. return !(x^(x<<delta>>delta));
  4. }

divpwr2

Compute x/(2^n), for 0 <= n <= 30 (Round toward zero)
为正数则为,为负数则为
替换成的符号位即可。注意若为负则,提取符号位需要,但可以直接作为减

  1. int divpwr2(int x, int n) {
  2. int sign=x>>31;
  3. return (x+((sign&1)<<n)+sign)>>n;
  4. }

negate

return -x
~x+1

  1. int negate(int x) {
  2. return ~x+1;
  3. }

isPositive

return 1 if x > 0, return 0 otherwise
需要判,而的符号位不变,所以同divpwr2中用提取的符号位即可。但有个问题是,所以还要特判否则不对。
更简单的是直接求符号位,然后来特判

  1. int isPositive(int x) {
  2. return (!(x>>31))&(!!x);
  3. }

isLessOrEqual

if x <= y then return 1, else return 0
如果不溢出就是isPositiveOrZero(y-x)!((y-x)>>31&1)。注意到y-x溢出只发生在异号,而异号可以直接判断大小。所以先求一下的符号位判断是否溢出即可。

  1. int isLessOrEqual(int x, int y) {
  2. int sx=x>>31&1, sy=y>>31&1;
  3. return ((!sy)&sx)|(!(sy^sx)&!((y+~x+1)>>31&1));
  4. }

ilog2

return floor(log base 2 of x), where x > 0
即求最高位的。最简单的方法是再最高位右边全填上,再用bitCount-1。
填充类似bangx|=x>>16,x|=x>>8,...,x|=x>>1,从高位传下来即可。也同bangx|=x>>1最后x|=x>>16都可。

还有种方法是,先求出高位是否有,有就加;然后再求当前位中高位是否有,有就加...直到最后一位。

  1. int ilog2(int x) {
  2. int tv1=0x55|(0x55<<8),v1=tv1|(tv1<<16);
  3. int tv2=0x33|(0x33<<8),v2=tv2|(tv2<<16);
  4. int tv3=0xF|(0xF<<8),v3=tv3|(tv3<<16);
  5. int v4=0xFF|(0xFF<<16);
  6. int v5=0xFF|(0xFF<<8);
  7. int res;//鍙よ€佺殑csapp鏍囧噯瑕佹眰C涓嚱鏁板0鏄庡繀椤昏鍦ㄦ渶鍓嶉潰
  8. x=x|(x>>16), x=x|(x>>8), x=x|(x>>4), x=x|(x>>2), x=x|(x>>1);
  9. res=(x&v1)+(x>>1&v1);
  10. res=(res&v2)+(res>>2&v2);
  11. res=(res+(res>>4))&v3;
  12. res=(res+(res>>8))&v4;
  13. res=(res+(res>>16))&v5;
  14. return res+~1+1;
  15. }

float_neg

Return bit-level equivalent of expression -f for floating point argument f.
实数取反,取反符号位即可。要特判NaN。

  1. unsigned float_neg(unsigned x) {
  2. if((x&0x7FFFFFFF)>0x7F800000) return x;
  3. return x^0x80000000;
  4. }

float_i2f

Return bit-level equivalent of expression (float) x
为负数,则先将取反;为可直接特判。
设最高位的为第位,则浮点数的阶码,小数子段则为低位(如果小于位则后面补,大于位则舍弃并进位,注意是向偶数舍入!)。最后再加上符号位。
具体:
用前面方法或循环找到最高位的,设其距第位距离为,则直接将ux=(unsigned)x左移位,使小数子段确定为位,舍弃位为位。
偶数舍入两种情况:1. 舍弃的位大于,即ux&0x1FF>0x100
2. 有效位和最高舍弃位均为,即第位和第位均为,即ux&0x300==0x300ux&0x3FF>=0x300。(我怎么总是忘了位是从开始标号)

最高符号位,加上从位开始的,最后加上小数子段ux>>9和进位即为答案。

  1. unsigned float_i2f(int x) {
  2. unsigned ux=x,sign=ux>>31,carry=0; int d=0;
  3. if(!x) return 0;
  4. if(sign) x=~x+1, ux=x;
  5. while(!(x&0x80000000)) x<<=1, ++d;
  6. ux<<=d+1;
  7. carry=((ux&0x1FF)>0x100)|((ux&0x300)==0x300);
  8. return (sign<<31)+((31-d+127)<<23)+(ux>>9)+carry;
  9. }

float_twice

Return bit-level equivalent of expression 2*f for floating point argument f.
如果是规格化数,直接阶码+1(直接加0x800000);
如果是非规格化数,只需将x左移1位(注意符号位)。
特殊值则直接返回。

  1. unsigned float_twice(unsigned x) {
  2. if((x&0x7F800000)==0x7F800000) return x;
  3. else if(!(x&0x7F800000)) return (x<<1)|(x&0x80000000);
  4. else return x+0x800000;
  5. }

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注