目录
一、前言
二、配置lab环境
三、开摆~
1.bitXor
2.tmin
3.isTmax
4. allOddBits
5.negate
6.isAsciiDigit
7.conditional
8.isLessOrEqual
9.logicalNeg
10.howManyBits
Float
1. floatScale2
2.floatFloat2Int
3.floatPower2
其实学长在布置作业的时候是有发了一个英文的文件,里面的README是有lab这个东西的简介的。但是里面打开就是全英,真的是懒了。所以完全没看就自己去csdn一顿乱搜。水了一下午的课可算是搞懂了来龙去脉,但当我会看readme时才发现自己确实浪费了许多没必要的时间,唉,下次一定~~
总的来说csapp是一本关于计算机的大杂烩,什么都讲,包括os 、计算机网络network 、计算机组成、汇编等,而lab则是这本书上配套的练习题,完成起来也是有一定的难度的,实验文档都是英文的,并且实验是建立咋了linux平台上的。
上图找的有道,可以明白了lab就是有许多题,我们要按照它的要求去具体实现。 先来进行lab1-datalab
一下午的时间还是有收获的,下面我们就一步一步的开始吧!
也看了很多关于配置环境文章,有简单需要安装docker,但对于我们小白,还是找个简单的吧
【CSAPP】lab0 环境的配置_刘_浩的博客-CSDN博客_csapp lab 环境配置【CSAPP】lab0 环境的配置引言我的环境配置折腾了一下午,其实很简单(前提是你已经安装过linux虚拟机)。整体分为三步:1. 下载源码2. 配置gcc环境3. 验证具体步骤1. 下载源码https://blog.csdn.net/qq_45703010/article/details/120897185这个是我找到的最简单的一个搭建环境的方法,这里附上一张我自己配好环境的图
可以看到当我们输入./btest -g 之后已经列出来我现在的lab1的答题情况,包括Score这道题的得分
Rating这道题的难度也是这道题的满分,Errors出现的错误。
注意事项
接下来就是最重要的注意事项,其中分为int题和float题,注意事项不同,主要根据题目上方的提示进行做题。
int题允许:
整数常量0到255 (0xFF)。不允许使用像0xffffffff这样的大常量。
函数参数和局部变量(没有全局变量)。
一元整型运算!~。
二进制整数运算& ^ | + << >>。不允许:
使用任何控件结构,如if、do、while、for、switch等。
定义或使用任何宏。
在此文件中定义任何附加函数。
调用任何函数。
使用任何其他操作,如&&,||,-,或?
使用任何形式的铸造。
使用除int以外的任何数据类型。这意味着不能使用数组、结构或联合。float题
对于需要实现浮点运算的问题,编码规则不那么严格。你可以使用循环和条件控制。你可以同时使用整数和无符号。
可以使用任意整数和无符号常量。可以对int或unsigned数据使用任何算术、逻辑或比较操作。
但被明确禁止:义或使用任何宏。
在此文件中定义任何附加函数。
调用任何函数。
使用任何形式的铸造。
使用除int或unsigned以外的任何数据类型。这意味着不能使用数组、结构或联合。
使用任何浮点数据类型、操作或常量。
首先我们打开这个文件
直接记事本形式打开就可以了,我们直接在里面修改,做题。之后在这个文件夹里打开终端,然后btest检查错误,订正。整体就是这样。我们先来看第一题:
/*
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) {
题意就是只使用~和&来实现x^y的效果。这里需要知道一个知识点异或等于取反的组合。
int bitXor(int x, int y) {
int a = ~(x&~y);
int b = ~(~x&y);
return ~(a&b);
用到数学里的公式(参考资料)
* tmin - return minimum two's complement integer
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 4
* Rating: 1
*/
int tmin(void) {
int a = 1;
return a << 31;
这里不知道怎么我的代码里已经写好了(学长忘删了)假装没看到。题意是要求返回一个二进制里的最小值。整个程序运行在32位系统上,所以我们要找32位里二进制的最小值。首先我们需要明确32位里有负数,但计算机里的正负是人为来界定的,我们来看下面这张图:
0111代表的是7,但是1000就代表了-8,这是四位的图像,32位图像也是一样的。最小值也是10000000000000000000000000000000(一共是32位)
但是注意事项里有提到,我么使用的整数常量只能是0到255 (0xFF),所以我们需要通过位操作来实现得到0x8000 0000。我们将1进行左移位,左移31位后就可以得到。
int tmin(void) {
int a = 1;
return a << 31;
/*
* 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) {
int res = x + 1;
int res2 = x & res;
return !res2;
}
判断给定值x是否为最大值
当x为最大值时,返回值为1,即在最后判断时需要逻辑判断!res(res为我们最后转化成的值)即res要为0,所以我们要把x转化成0。
假设此时x为最大值,则x=0x7FFF FFFF ; x+1=0xF000 0000 ;令a=x+1 则x+a=0xFFFF FFFF。二进制表示为全1.取反后为0
代码表示为:a=x+1 ; res=a+x ; res=~res ; !res
测试一下发现不对,忽略了特殊情况x=0xFFFF FFFF ;a=x+1=0x0000 0000 ; x+a=0xFFFF FFFF取反之后得到0x00000000 所以要排除x=0xFFFF FFFF 参考网上:
int isTmax(int x) {
int a=x+1;
x=a+x;
x=~x;
a=!a;
res=x+a;
return !res
}
/*
* 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) {
return 2;
这一题要求如果x的奇数位都为1,则返回1:否则返回0
只说了奇数位为1没有说偶数位,所以我们将x先和0xaaaa aaaa(奇数位全是1)进行与运算,运算之后如果偶数位位0则还为0,若偶数位为1则变为0,即偶数位全为0。但是如果奇数位为0则变为0,为1则还是1。最后将结果再与0xaaaa aaaa进行异或运算。如果结果为0则符合,否则不符合
代码实现如下
int allOddBits(int x) {
//这里我们不能直接用0xaaaa aaaa,所以先移位把它搞出来
int a = 0xaa;
int a_8 = a<<8;//0x0000aa00
int low_16 = a|a_8;//0x0000aaaa;
int high_16 = low_16<<16;//0xaaaa0000
int num = low_16 | high_16;//0xaaaaaaaa
int check = (x&num)^num;
//如果check是0 则满足要求 返回1
return !check;
* negate - return -x
* Example: negate(1) = -1.
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 5
* Rating: 2
*/
int negate(int x) {
return ~x + 1;
}
这一题要求返回一个数的负值,这里我们要知道不管是正数还是负数,我们对其取反再加1都等于它的相反数。所以答案就是:
int negate(int x) {
return ~x + 1;
}
/*
* 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 2;
}
嗯!意思就是如果x在大于等于0x30且小于等于0x39就返回1;否则,返回0。这里需要判断啊。首先我们需要明确什么是逻辑取反”!“,什么是按位取反”~“。
逻辑取反”!“:它是不看你具体是什么数字的,只要数字是非零数字都会返回0,并判断为假。如果是0,会返回1,并判断为真。
按位取反”~“:这个就是位运算里的取反,如4,二进制表示为0100,取反后为1011也就是11,每个位的1变为0,0变为1.
清楚这个之后,以我现在的理解,!常用于最后return,也就是返回时进行判断。
回到这个题x需满足条件:0x30= x-0x30>=0------>(x+~0x30)=a 0x39-x>=0------>(0x39+~x)=b 此时我们只需要判断符号位了,当a,b均大于等于0时,a和b的符号位肯定为0。所以我们分别将a,b右移31位,当a,b均为0时,判断为真。则答案为: 题目要求设计函数来实现x?y:z。首先我们需要知道什么是x?y:z.查找了资料了解了。这是一个三目运算符,这个符号实现下面一个选择分支结构: min=y; else min=z; 所以说它是一个条件表达式,当x判断为真时,表达式取值为y,假时取值为z 首先思路就是最后要进行一个或运算,x要返回一个值,当x为真时我们可以得到y的值此时z值要为0;当x为假时,y会变为0,而得到z的值。最后变为0的操作我们需要用到&,和0x0000 0000进行与运算,则任何数都会返回为0。就是说到最后时x变为0xFFFF FFFFF,0x0000 0000分别和y,z进行&运算。看题目给的Example,当x为2时,返回4也就是y。所以当x为非0数时返回y,则当x为0时返回z。代码如下: 实现一个小于等于符号来判断两个数。当x<=y时返回1,否则返回0。转化一下,满足条件y-x>=0时就行。当x,y同号是我们直接向减判断,但是当x,y不同号时,我们需要考虑是否会溢出。 溢出:正-负=正+正。若超过最大正数范围则会正溢出,即正+正=负 负-正。若超过最小负数范围则会负溢出,即负+负=正 所以需要分情况:1.x,y符号位相同 2.x,y符号位不同 题意是当x不为0时,返回为0,当x为0时,返回为1;像区别正负和0的题我们要先找它的符号位: 首先当x不为0时,我们怎么变换将它转化为0?我们将x和x的相反数进行异或即((~x+1)^x)再进行右移31位,x不为0时得到-1,x为0时我们得到0。最后加1就可以实现函数了。 这道题根本看不懂(#-.-)参考的网上: 函数功能:判断x使用补码需要多少位来表示 传入函数uf并且返回2*uf,首先我们先看一下浮点数的分类: 不同类型乘2格式是不一样的,所以我们需要分这四种来分别讨论: 我们定义exp来代表阶码,当uf为规格化时我们只需要exp加1就能实现uf。当uf为非规格化时我们只需要整体左移(这里还是不理解)。当uf为无穷大和无穷小或者NaN时返回它本身。 具体代码如下: 函数功能:将uf转换为int类型 传入x,返回2.0^x,以unsigned代替所要表示的float型。 磕磕绊绊总算完成了,艰难但也收获很多。对一些基础知识更加的熟练。int isAsciiDigit(int x) {undefined
return !((x + ~0x30)>>31) | ((0x39 + ~x)>>31));
}
7.conditional
/*
* 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 2;
if(x)int conditional(int x, int y, int z) {undefined
x = ~(!!x) + 1;
//通过此运算当x为非0数时,x值会变为0xFFFF FFFF
//而x为0时,还会变成0
return (x & y) | (~x & z);
}
8.isLessOrEqual
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) {
return 2;
}
太难了。。。借鉴的大佬的:int isLessOrEqual(int x, int y) {
int m, n, z;
m = x>>31 ^ y>>31;//当x和y异号时m=1
n = !((x ^ ~y) + 1) | (((x + (~y + 1)) >> 31) & 1);//两者相等或x - y < 0
z = (x>>31) & 1;
return (m & z) | (~m & n);
}
9.logicalNeg
/*
* 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 2;
}
10.howManyBits
思路:对于正数,需要寻找的是最后一个为1的位,对于负数,需要寻找的是最后一个为0的位,为了将正负数都统一为只需寻找最有一个为1的位对x进行以下处理x = (~sign & x) | (sign & ~x),其中sign为x>>31。如果x为正数则,sign=0x0,如果x为负数,sign=0xffffffff。所以对于上面x的处理的意思为,如果x为正数,x保持不变,如果x为负数,对x进行按位取反。对于一个数要判断它的最后一个1在哪位,可以利用二分的思想处理。一个int类型的数32位,先对查找这个数的16-31位,如果存在则继续查找24-31位。如果这个数的16-31位不存在1,那么到8-15位去查找,以此类推。在实现中可以使用!操作快速地判断某一段中是否存在1,例如在判断16-31位时可以使用b16 = !!(x >> 16) << 4来获取下次要移位的位数,如果存在1,那么下次要右移16位,对高16位进行判断,如果不存在,那么下次右移的位数为0,判断低16位。int howManyBits(int x) {undefined
int sign = x >> 31;
x = (~sign & x) | (sign & ~x); // 如果x为正数保持原型,如果x为负数取反,使得非符号位最高位为1
int b16 = !!(x >> 16) << 4; // 如果大于16位,那么b16 = 16否则为0
x >>= b16;
int b8 = !!(x >> 8) << 3; // 如果大于16位那么此时取的是16-23位,否则为0-7bit
x >>= b8;
int b4 = !!(x >> 4) << 2;
x >>= b4;
int b2 = !!(x >> 2) << 1; // 0-1
x >>= b2;
int b1 = !!(x >> 1);
x >>= b1;
int b0 = x;
return b16 + b8 + b4 + b2 + b1 + b0 + 1;
}
Float
1. floatScale2
/*
* 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) {
}
情况
对应的数值
规格化的
(−1)s×2e−127×1.b22b21b20……b3b2b1b0
非规格化的
(−1)s×2−126×0.b22b21b20……b3b2b1b0
无穷大和无穷小
s=0:+ ∞ s=1:-∞
NaN
Not a number
unsigned floatScale2(unsigned uf) {
unsigned flag = uf>>31;//logic right shift flag_is_1_or_0
unsigned exp = (uf>>23)&0xff;//提取阶码 去除符号位
//unsigned last_num = uf&0x7fffff;//提取尾数
//规格化的 直接阶码加一再替换原来的阶码就好
int res_exp = (exp + 1)<<23;
int res = uf & 0x807fffff;//1000 0000 0111
//无穷大和nan都返回自身
if(exp == 255)
return uf;
//非规格化的
if(exp == 0)
{
uf = uf << 1;
uf = uf | (flag<<31);
return uf;
}
//规格化的 直接阶码加一再替换原来的阶码就好
return res | res_exp;
}
2.floatFloat2Int
思路:获取指数域exp、符号位sign和尾数域frac。对于规格化数需要将frac的第24位置为1,所以frac = (uf & 0x7fffff) | 0x00800000,获取指数域的十进制值exp = ((uf & 0x7f800000) >> 23) - 127。将浮点数转换为整数的过程相当于将frac中隐藏的小数点右移exp位,同时将小数点后面的数值截断。如今小数点位于23位处,如果exp大于23,那么需要将frac左移exp-23位,如果exp小于23需要右移23-exp位把小数点截掉,最后将frac转换为补码表示。
* 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 flag = uf>>31;//logic right shift is_1_or_0
unsigned exp = (uf>>23)&0xff;//提取阶码 去除符号位
unsigned last_num = uf&0x7fffff;//提取尾数
//规格化的
int real_exp = exp - 127;//移码减去偏置 这里一定要是int型 不然没办法判断是否大于0 因为unsigned都大于等于0
unsigned real_last_num = last_num | 0x800000;//添上隐式的1
//无穷大和nan
if(exp == 255)
return 0x80000000;
//非规格化的
if(exp == 0)
return 0; //非常接近0的小数 直接舍弃小数变为0
//规格化的
if(real_exp >= 0)
{
if(real_exp>=31)//overflow
return 0x80000000;
real_last_num = real_last_num >> (23 - real_exp);//移码的意义 而且对于小数部分舍弃了
if(flag)//若是负数 还要转为补码形式
real_last_num= ~real_last_num+1;
return real_last_num;
}
//real_exp<0 则说明是绝对值小于1的数 直接返回0
return 0;
}
3.floatPower2
提取符号位、阶码、尾数,再按上述的4类情况分类讨论,具体步骤在代码中的注释写得很清楚,搞清楚分界值到底该如何选取是关键unsigned floatPower2(int x) {
if(x>127)//overflow
return 0x7f800000;//+inf 符号位0 阶码全1 尾数全0
//规格化的 [-126,127]
if(x>=-126)
{
unsigned exp = x+127;//加上bias
return exp<<23;//只需改变阶数
}
//非规格化的 [-149,-127]
if(x>=-149)
return 0x1<<(x+149);//只需改变尾数
//x<-149 too small to be represented
return 0;
}