CSAPP 之 DataLab实验

前言:CSAPP的DataLab的实验

因为一直在学习计组,已经在系统的学习原码/反码/补码/定点/浮点的相关的知识点了,这里这里打算肝完CSAPP的相关实验巩固自己的计组的知识

BitXor

其实题目的要求就是通过xor转化为一个用&~来进行表达的一个式子

/*
139 * bitXor - x^y using only ~ and &
140 * Example: bitXor(4, 5) = 1
141 * Legal ops: ~ &
142 * Max ops: 14
143 * Rating: 1
144 */

我这里的解决方法是通过画卡诺图然后通过德摩根定律来进行转化的,如下图所示

我给出的代码如下所示

int bitXor(int x, int y) {
return ~((~((x)&(~y)))&((~((~x)&(y)))));
}

可以看到是可以成功运行的

tmin

题目要求通过! ~ & ^ | + << >>来返回一个最小的补码整数

/*
149 * tmin - return minimum two's complement integer
150 * Legal ops: ! ~ & ^ | + << >>
151 * Max ops: 4
152 * Rating: 1
153 */

最小的补码整数在32位的cpu中就是0x80000000,那么这里的话通过1<<31即可实现运算

isTmax

题目要求如下,要求实现通过! ~ & ^ | +来求出一个值是否是补码整型的最大值,如果是的话那么返回1,如果不是的话那么返回0

8 /*
159 * isTmax - returns 1 if x is the maximum, two's complement number,
160 * and 0 otherwise
161 * Legal ops: ! ~ & ^ | +
162 * Max ops: 10
163 * Rating: 1
164 */

这里的话我一开始的思路就直接是return (((1 << 31) -1)==x ? 1 : 0);,但是这样子直接是不行的,违反了题目的操作符要求,如下图所示

所以这里的话只能是别的方法,首先需要知道的是32位补码中 最大值0x7fffffff和最小值0x8000000相差为1,所以(0x7fffffff+1)再取一个反的话那么又是0x7fffffff自身

所以就可以通过这个方法来进行判断,return !(~(x+1)^x) && (!!(x+1))

最后合并的话那么就是!(~(x+1)^x) & (!!(x+1));

这里第一个知识点就是异或的知识点

  • x ^ 1 的作用是得到 自身二进制相反的数

  • x ^ 0 的作用的得到 自身

  • 一个数 异或 自身 得到的就是 0

第二个就是!!的知识点,这个可以作为验证当前一个数是0还是非0的一种方法

  • 如果一个非0的数取两次!得到1

  • 如果是0的话,那么它取两次!得到的就是0

allOddBits

题目要求如下,要求实现通过! ~ & ^ | + << >>来求出一个十六进制的奇数位是不是全为1,如果都是的话,那么返回1即可

/*
* 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
*/

我的实现如下,我的思路就是0XAAAAAAAA自身就是奇数位全为1的数,那么我将其他数跟这个数来进行比较即可,这里利用异或的特性,如果与出来的结果不是0xAAAAAAAA,不是的话异或0xAAAAAAAA那么就不是0,是的话就是1,而题目要求是的话返回1,所以再取一次非即可

int allOddBits(int x) {
int w = ((0xAA << 8|0xAA)<<8|0xAA)<<8|0xAA;
return !((x&w)^w);
}

操作数符合如下所示

negate

题目要求说如下所示,就是给一个数,返回的值是对应数的一个负数形式

允许的操作符为! ~ & ^ | + << >>

/*
181 * negate - return -x
182 * Example: negate(1) = -1.
183 * Legal ops: ! ~ & ^ | + << >>
184 * Max ops: 5
185 * Rating: 2
186 */

我的想法其实就是改变其符号位即可,但是如果只是简单的改变符号位,其实这种仅限于原码存储的情况下,对于原码来说,一个数的负数只需要改变其原码即可

但是在计算机中存储的都是补码的形式,所以这里只需要找到补码的一个数的正数和负数之间的关系即可

就比如拿-1 +1 , -1 -> 1,1111111 +1 -> 0,0000001 可以看到 +1 想要到 -1 可以通过 取反+1即可拿到-1这个数

那么最终直接可以写

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

操作符检测是如下所示

isAsciiDigit

题目要求如下所示,让你判断一个数的十六进制是不是 [0x30,0x39] 的区间范围之内

1 /*
192 * isAsciiDigit - return 1 if 0x30 <= x <= 0x39 (ASCII codes for characters '0' to '9')
193 * Example: isAsciiDigit(0x35) = 1.
194 * isAsciiDigit(0x3a) = 0.
195 * isAsciiDigit(0x05) = 0.
196 * Legal ops: ! ~ & ^ | + << >>
197 * Max ops: 15
198 * Rating: 3
199 */

我的想法就直接将其分为两个部分来进行计算,一个是高位的0x3,还有一个是低位[0,9]

第一个部分很好解决,直接可以通过!((x>>4)^3))来进行处理,先拿到高4位的值然后与0x3异或处理,如果本身是0x3的话,那么取非就是1,不是的话就是0

然后就是第二部分,我借鉴了别人的实现 (!(x^(x&0x37))|(!(x^(x&0x39)))

!(x^(x&0x37)),这部分就是判断0-7之间的,如果是该区间的话那么返回的就是1

(!(x^(x&0x39))),这部分就是判断8-9之间的,如果是的话那么返回的就是1

0x37 -> 0011 0111, 0111 正好包含了 0-7之间的二进制,如果一个数与 0111的话,如果在该范围内,那么结果是不变的,也就是还是自身,那么如果自身的话那么异或自身就是0了,取非就是1了

0x39跟上面也是同理

int isAsciiDigit(int x){
201 // 1010(A) & 0111(7) = 0010
202 // 0111 & 0011 = 0011
203 // return (!((x>>4)^0x3))&(!!(((x&0xF)+(~0xA+1))>>31));
204 return (!((x>>4)^3))&(!(x^(x&0x37))|(!(x^(x&0x39))));
205 }

检查操作符如下所示

conditional

题目要求如下,其实就是通过位运算来实现三目运算符的效果,操作符要求! ~ & ^ | + << >>

/*
207 * conditional - same as x ? y : z
208 * Example: conditional(2,4,5) = 4
209 * Legal ops: ! ~ & ^ | + << >>
210 * Max ops: 16
211 * Rating: 3
212 */

我自己的想法是这样子的,既然是三木运算符,那么肯定需要有一个中间的条件来进行分隔,那么x变量肯定是作为分割线的,所以这里将其取两次非,要么是0要么就是1

但是我不知道这个需要怎么用才能让y和z如果非0的话就取y,0的话就取z,同样的这里我也借鉴了别人的实现

这里最重要的是~x+1这个值,通过这个值就能拿来将y和z进行分开计算,要么取y要么取z,我实在想不到啊...

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

操作符检测如下图所示

isLessOrEqual

题目要求如下,如果x<=y的话,那么就返回1,否则返回0

/*
219 * isLessOrEqual - if x <= y then return 1, else return 0
220 * Example: isLessOrEqual(4,5) = 1.
221 * Legal ops: ! ~ & ^ | + << >>
222 * Max ops: 24
223 * Rating: 3
224 */

我的解决方法如下所示

  • 判断两个数的符号位不同的话,那么就当y是正数,x为负数的情况下,那么如果满足!x&y的情况,那么就是y>x

  • 同时还需要判断两个数是否相等,如果相等的话同样返回为1,所以就是 !(x^y),如果x和y相等它们异或的结果肯定是为0,取反就是1

  • 如果两个数的相同的话,那么就需要通过相减来获得结果,而这个减的操作符没有,那么这里求出[y]补的[-y]补即可,那么就是~y+1,这样x-y转化为x+[-y]补码实现了相同的操作

int isLessOrEqual(int x, int y) {
226 int xHigh = x >> 31;
227 int yHigh = y >> 31;
228 int xyRes = (x+((~y)+1)) >> 31; // 0 0
229 return (!(x^y)) | (xHigh & (!yHigh)) | ((!(xHigh^yHigh))&(xyRes));
230 //return (!(x^y)) && (!!(x&y) & (!(x&y)));
231 }

操作符检查如下所示

posted @   zpchcbd  阅读(394)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
点击右上角即可分享
微信分享提示