csapp实验摘选 I Data Lab ——小小菜下士的第一篇博客

csapp实验摘选 I Data Lab ——小小菜下士的第一篇博客

注:这是我的第一篇博客,试图在通往程序猿的路上踏出坚实的一步。
——小小菜下士

实验来自: 【读厚 CSAPP】I Data Lab

(一)任务目标:
主要是通过这次的作业来熟悉整型及浮点数的位表达形式,简单来说,就是解开一些人工谜题。共6个谜题,我选择其中的若干个进行练习,选择的谜题如下:
一:比特操作谜题:
1.isAsciiDigit(x) :x 是 ASCII 码吗 难度3 15
2.conditional(x, y, z) :类似于 C 语言中的 x? y:z 难度3 16
3.logicalNeg(x) :计算 !x 而不用 ! 运算符 难度4 12
二:整数运算谜题:
1.negate(x) :不用负号得到 -x 难度3 24
2.isLessOrEqual(x,y) :x <= y? 难度3 24
3.howManyBits(x) : 计算表达 x 所需的最少位数 难度4 90

任务指引还是比较清晰的,主要有以下一些说明:

整型的范围是 0 到 255(0xFF),不允许用更大
只能包含参数和局部变量
一元操作符 ! ~
二元操作符 & | + << >>

不允许的操作有:

使用任何条件控制语句
定义和使用宏
定义其他的函数
调用函数
使用其他的操作符
使用类型转换
使用除 int 之外的类型(针对整型)
使用除 int, unsigned 之外的类型(针对浮点数)

可以认为机器:

使用 2’s complent,32位
执行算术右移
移动超过字长的位数会出问题

(二)具体实验及说明分析
一:比特操作谜题:
1.isAsciiDigit(x)

short isAsciiDigit(x){
    short y;
    y=!(x>>7)//右移7位判断x的第八位是否为1;
    return y;
}
// bits谜题1.isAsciiDigit(x):x 是 ASCII 码吗?	难度3
int main()
{
    short x,y;
    scanf("%hd",&x);
    y=isAsciiDigit(x);
    if(y==1)  printf("x is a Ascii dight!\n");
    else printf("x is not a Ascii dight!\n");
    return 0;
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
由于Ascii 对应的十进制数值范围为0—127,二进制表示为0000 0000到0111 1111,所以只要判断第八位是否为1就能知道输入的short 型标量是否为Ascii值,右移7位即可(x>>7)。

2.conditional(x, y, z)
题目要求:same as x ? y : z
例如:conditional(2,4,5) = 4
允许操作:! ~ & ^ | + << >>
操作数限制:16

int conditional(int x, int y, int z){
    int exp=0;//exp判断变量
    exp=~!x+1;//若x为0,!x为1,~!x+1为0xffffffff;
    //若x不为1,!x为0,~!x+1为0x0
    return (y&~exp)|(z&exp);

}
int main()
{
    int x=0,y=0,z=0,k=0;
    scanf("%d,%d,%d",&x,&y,&z);
    k=conditional(x, y, z);
    printf("%d",k);
    return 0;
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
由于不能使用条件语句,而函数返回的值与y和z有关,所以要构造一个结构为(y&op1)|(z&op2)的表达式,op1与op2由x决定,当x为0时,op1=0x0.op2=0xffff ffff;当x不为0时,op1=0xffff fff,op2=ox0;可想到op1与op的关系,op1=~op2;则有标志标量exp= ~!x+1符合条件。
由代码结果看出,当x为0时,输出第二个数z;当x不为0时,输出第一个数y。
这与x ? y : z功能一致。

3.logicalNeg(x)

int logicalNeg(int x){
   int value=~((x^(~x+1))|(x&(~x+1)));
   return 1&(value>>31);
}

int main()
{
   int x,k;
   scanf("%d",&x);
   k=logicalNeg(x);
   printf("%d\n",k);
   return 0;
}

这个很难,关键是找到表示出1或0的表达式。假如某个非0数x二进制表示为
…1…0(这个1表示最后一个低位不为0的1,1后全为0)x与其补码异或后即x^(~x+1)会有如下形式

1.x:			......(前k位)10...
2.~x+1:  	    .取反..(前k位)“1”0...
3.x^(~x+1):   1..1(前k位)“0”0..0
4.对第三行取反:0000(前k位)1...11
5.对第四行右移31位,得到0

1)一般情况:此时可以试验下,取…(前30位随意)10(后两位),~x+1=…(前30位随意取反后)10(这里求补码的一个等价方法就是:从右往左数,数到第一个 1 ,这个1前面的取反,其余不变),x^ ( ~x+1)=1111…(前30位为1)00(后两位为0),然后再取反得到0000…(前30个位为0)11(后两位为1),右移31位后得到0。那是不是这样就可以了呢?
2)特例:想到了补码当中有个特例,那就是最高位为1其余位均为0的二进制数,比如int型的100…(31个0),它的补码是其本身,这样一来异或之后的前k位均为0,在取反后前k位均为1,右移31位后会得到1,这样与其要求应该得到的!x=0不符合,所以这个特例得处理一下;
为了专门解决这个特例,需要 |(或) 上一个表达式x&(~x+1),情况如下

1.x:   		10000...(31个0)
2.~x+1:		10000...(31个0)
3.x&~x+1:	10000...(31个0)

整个运算情况如下:

1.x:   		10000...(31个0)
2.~x+1:		10000...(31个0)
3.x^(~x+1):00..00(32个0)
4.x&(~x+1):	1000..(31个0)
5.x^(~x+1) |  x&(~x+1) :100..(31个0)
6.~(x^(~x+1) |  x&(~x+1)):0000(31个0)1
7.右移31位:0(奈斯!)

,上图表明可以解决100…这个情况,hahahahaha,只剩下x=0的情况:x=0

1.x:   		00...(32个0)
2.~x+1:		00...(32个0)
3.x^(~x+1):00..(32个0)
4.x&(~x+1):	00..(32个0)
5.x^(~x+1) |  x&(~x+1) :00..(32个0)
6.~(x^(~x+1) |  x&(~x+1)):111..(32个1)
7.右移31位:111..(32个1)
8.1&(111..(32个1))=1   (奈斯*2)

1&()表示取最低位,也就是符号位。上图中1&“结果值”x=0也是符合的!!!
运行下看看在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
太好了是对的!
二:整数运算谜题:

  1. (int) negate(x)
    题目要求:return -x
    例如:negate(1) = -1.
    允许操作:! ~ & ^ | + << >>
    操作数限制:5
int negate(int x){
    x=~x+1;
    return x;
}
int main()
{
    int x=0,k=0;
    scanf("%d",&x);
    k=negate(x);
    printf("%d\n",k);
    return 0;
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
有个公式-x=~x+1,按照这个公式写代码就可以了。

2.isLessOrEqual(x,y)

int isLessOrEqual(int x, int y){
    int value ;
    value=((x+(~y+1))>>31)|~(x^y);
   // x-y即表示为x+(~y+1),右移31位
    //(x^y)判断两个数是否相等;
    //如果相等x^y=0,取反后为1;
    //最后的结果为1表明x<=y;为0表明x>y;
    return 1&value;
}
int main()
{
    int x,y,k;
    scanf("%d,%d",&x,&y);
    k=isLessOrEqual(x,y);
    printf("%d\n",k);
    return 0;
}

程序目的是判断x<=y是否成立,若x<=y,则返回1,否则返回0。
1:x=y容易判断,若x=y,则有x^y=0,即~(x ^y)=1;
2:x<y或者x>y,则用减法判断,由于不能使用“-”号,采用~y+1的形式表示-y,则x-y表示为x+( ~y+1);判断此式的正负性只要判断符号位就行,若符号位为1,则为负数,若为0,则为正数;采用右移31的方式取符号位,
即(x+( ~y+1))>>31),结果为0xffffffff,即-1则表明x<=y,与1做&运算得到1;结果为0x0,则表明x>y,与1做&运算得到0;
3.综合1与2可知表达式为value=((x+( ~ y+1))>>31)|~(x^y);return 1& value;
下面测试下:在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
是的都对了!

3.howManyBits(x) :
题目要求:return the minimum number of bits required to represent x in two’s complement
例如:
howManyBits(12) = 5
howManyBits(298) = 10
howManyBits(-5) = 4
howManyBits(0) = 1
howManyBits(-1) = 1
howManyBits(0x80000000) = 32
允许操作:! ~ & ^ | + << >>
下面解释整个代码:
1.temp的作用:x为正数temp就是x,x为负数temp就是按位取反,x=0,temp=0; isZero的作用:用来判断x是否为0,如果x=0,则 isZero=1;如果x!=0,则 isZero=0; notZeroMask的作用:非0标志,x!=0则notZeroMask = 0xffffffff,即-1;
2. int bit_16,bit_8,bit_4,bit_2,bit_1:当x有足够位数时值分别为16,8,4,2,1
以下用bit_16解释:如果高16位有值,需要的位数至少为16(极端情况就是高16位为0…(15个0)1);如果有值,则需要右移16位来确定x的高16位是从哪一位开始有值,temp=temp>>bit_16表示右移16位;如果高16位没有值,则意味着全0,同样右移16位来寻找最高有效位。
之后右移8位右移4位右移1位与上面类似进行;
3.temp=bit_16+bit_8+bit_4+bit_2+bit_1+2:此处有点难以理解,需要一个符号位,当使用bit_1右移一位时,有两种情况,10和11,右移一位后还有一位1,此时需要加上1;另外需要一个位表示符号位,因此再加1,所以总共+2。

int howManyBits(int x) {
    int temp=x^(x>>31);//x为正数temp就是x,x为负数temp就是按位取反,x=0,temp=0;
    int isZero=!temp;//如果x=0,则 isZero=1;如果x!=0,则 isZero=0;
    int notZeroMask=(!(!temp)<<31)>>31;//notZeroMask is 0xffffffff
    int bit_16,bit_8,bit_4,bit_2,bit_1;
    bit_16=!(!(temp>>16))<<4;
    //see if the high 16bits have value,if have,then we need at least 16 bits
    //if the highest 16 bits have value,then rightshift 16 to see the exact place of
    //if not means they are all zero,right shift nothing and we should only consider the low 16 bits
    temp=temp>>bit_16;
    bit_8=!(!(temp>>8))<<3;
    temp=temp>>bit_8;
    bit_4=!(!(temp>>4))<<2;
    temp=temp>>bit_4;
    bit_2=!(!(temp>>2))<<1;
    temp=temp>>bit_2;
    bit_1=!(!(temp>>1));
    temp=bit_16+bit_8+bit_4+bit_2+bit_1+2;//至少还需要1位,10与11是一样的(at least we //need one bit for 1 to tmax,
    //and we need another bit for sign
    return isZero|(temp&notZeroMask);
}
int main()
{
    int k,x;
    scanf("%d",&x);
    k=howManyBits(x);
    printf("%d\n",k);
    return 0;
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
ok啦,差不多就是这样。感谢你能阅读到文末,看我写的这么繁琐的东西,good luck!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值