CSAPP Datalab 个人学习记录及部分题解

实验报告

part1 解题思路

1.bitxor(实现异或)

容易想到,bitxor的定义就是(x&~y)|(~x&y),再根据离散数学学过的德摩根律,
~(a|b)=(~a)&(~b),带入即可转化为~((~(x&~y))&(~(~x&y))),很不错做出来了,是用了8个操作符;

不过想想这么一坨看起来很累赘,换个思路,我们要使(x&~y)|(~x&y)成立,其实可以否定另外两种情况———x&y~x&~y,于是改进为这样的代码:~(~x&~y)&~(x&y),这样用了7个操作符(好吧也没差多少

2.tmin(最小补码数)

根据定义是1000000…所以1<<31,没什么好说的。

3.isTmax(是不是最大补码数)

最大补码数长这样:01111111…,本来想直接取反跟Tmin异或来着,发现不让用<<。。。

换个思路,发现Tmax加一后取反为自己,这样的数只有Tmax和111111111111(-1),于是写出了这样的代码

!!是很用的,可以把一切非0值转换为1:

int j = x + 1;
return !(x ^ ~j) & !!j;

意思就是x^~j为全零且j不为0(x不为-1)

进一步优化代码,把!提出来,得到

int j = x + 1;
return !((x ^ ~j) | !j);

一共6个操作符。

4.allOddBits(奇数位为全一)

意思就是问输入的是不是1x1x1x1x1x1x…1x1x这样的数,x位置随便。

这个运算比较像|,那么构造01010101010...10101x进行|,如果得到11111111111(-1)就ok了,构造这样的代码:

int tmp = 0x55 + (0x55 << 8);
tmp += tmp << 16;//tmp应该是01010101...010101
return !((tmp | x) + 1);
5.negate(相反数)

根据数字逻辑知识,取反加一就可以得到相反数,return ~x+1 ;

6.isAsciiDigit(是不是ascii里的0~9)

要判断'0'<=x<='9',只需要与对应值做差,再看符号位就行了。

那么应该是x-'0'符号位为0且x-'9'-1符号位为1;代码这样:

int b = ~(0x3A) + 1;//b为-('9'+1)
return (!((x + 10 + b) >> 31)) & ((x + b) >> 31);
7.conditional(模拟三元运算符)

代码如下:

int conditional(int x, int y, int z) {
    x = !!x;
    x = (~x) + 1;
    return (x & y) | (~x & z);
}

首先把x转换为非零的1或者0,考虑用1111以及0000这样的掩码对输出处理,想到对x取反加一可以得到-1(111111…1111)和0(0000000…00000),那这就非常的好了。

8.isLessOrEqual(x<=y ?)

思路其实很简单,判断y-x的符号位即可,但仔细一想,要考虑到溢出问题,所以再用符号位判断一下。如果符号位不同,那么可以直接得出答案;如果符号位相同,那么一定不会溢出,由y-x符号位得出答案。

敲代码的时候发现不考虑溢出居然也能过,看来是linux这个环境的问题(?),跟同学讨论发现居然真有人忘了,de了一圈bug。。看来是会有人忘考虑溢出的,请多多hack一下(不是)

放代码:

int isLessOrEqual(int x, int y) {
    int xsign, ysign, signsame;//x符号,y符号,符号是否同
    xsign = x >> 31;
    ysign = y >> 31;
    signsame = xsign ^ ysign;//same is 0
    return ((!ysign) & xsign) | !(signsame | ((y + ~x + 1) >> 31));
}

解释一下,如果xsign为0,ysign为1,那么((!ysign) & xsign)会得到0,反之xsign为1,ysign为0,可以返回1。

signsame保证了如果x,y符号不同,后面的算式!(signsame | ((y + ~x + 1) >> 31)一定会返回0,不会干扰前面的判断。如果相同,则signsame为0,结果取决于y-x符号位。

9.logicalNeg(表示

要实现0输出1,其他输出0,0有什么特点呢?取反加一还为零。我是偶然想到了取反加一,这样除了0和1000…000,其他互为都为相反数。(x | (~x + 1)) >> 31可以使一切非零的数得到11111111…111,而0会得到0,那么只需要再加一就可以实现逻辑了。最终为

return ((x | (~x + 1)) >> 31) + 1;

10.howManyBits(用补码表示会要多少位?)

这题是做的有点笨的,先找一波规律:

注意考虑符号位

在这里插入图片描述

由于我们实际上做的是对负数取反但不加1,对正数不变,因此构建sign=x>>31,以及那么x = x ^ sign;就可以使负数取反而正数不变。这是关于负数的处理,下面来看代码

int howManyBits(int x) {
	int sign, x16, x8, x4, x2, x1;
    sign = x >> 31;
    x = x ^ sign;
    x16 = (!!(x >> 16)) << 4;
    x = x >> x16;
    x8 = (!!(x >> 8)) << 3;
    x = x >> x8;
    x4 = (!!(x >> 4)) << 2;
    x = x >> x4;
    x2 = (!!(x >> 2)) << 1;
    x = x >> x2;
    x1 = x >> 1;
    x = x >> x1;
    return x16 + x8 + x4 + x2 + x1 + x + 1;
}

思路采用二分,以这个为例

x16 = (!!(x >> 16)) << 4;//x16判断第16位及更高是否有1,如果有的话x16=16.
x = x >> x16;//右移x16位,这样x16是否成立不会干扰接下来的判断。

就这样一直分分分,直到把一个两位数分为x1,剩余x必定为0或1。

最终结果就是x16 + x8 + x4 + x2 + x1 + x + 1,注意符号位所以加了一个1,美美收工。

11.floatScale2(2*uf=?)

先贴代码:

unsigned floatScale2(unsigned uf) {
    int exp, s;
    s = uf & 0x80000000;
    exp = uf & 0x7f800000;
    if (exp == 0)
        return (uf << 1) + s;
    if (exp == 0x7F800000)
        return uf;
    exp += 0x800000;
    if (exp == 0x7F800000)
        return exp + s;
    return exp + (uf & 0x807fffff);
}

思路就是先处理得到s和exp,s得到了1000000…或者000000…

exp = uf & 0x7f800000;把除了exp位置的地方都置为0

如果exp==0,那么直接return (uf << 1) + s;就算由非规格化小数变为规格化也是正确的,不得不说浮点数的设计十分巧妙。

如果exp==0x7F800000也就是实际上exp为265,那么直接返回原来的值uf就可以。

现在让exp+=0x800000也就相当于加一,如果为265则溢出,返回exp+s(无穷),否则返回exp + (uf & 0x807fffff),就这样。

12.floatFloat2Int(浮点变补码)

代码:

int floatFloat2Int(unsigned uf) {
    int s,E,M;//这次是对应位置的s,E,M
    s=uf>>31;
    E=((uf&0x7f800000)>>23)-127;//真实值
    M=(uf&0x7fffff)|0x00800000;//真实值(已经加一)
    if(E>30)//需要把1.___往左移动31位及以上,必定溢出。
        return 0x80000000;
    if(E<0)// 真实值为1.___居然还敢右移,直接给你返回0
        return 0;
    if(E<23)//如果E<23那么实际上M应该右移
        M>>=(23-E);
    else//反之左移
        M<<=(E-23);
    if(s)//如果符号位是1(负数)
        return -M;
    else//反之
        return M;
}

如果是非规格数,那么在这个函数会返回0,并没有问题。

感觉写的挺明白的,直接看就好了

part2 运行结果

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值