csapp-lab1

目录

一、前言

二、配置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时才发现自己确实浪费了许多没必要的时间,唉,下次一定~~watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAUlNBLg==,size_20,color_FFFFFF,t_70,g_se,x_16

总的来说csapp是一本关于计算机的大杂烩,什么都讲,包括os 、计算机网络network 、计算机组成、汇编等,而lab则是这本书上配套的练习题,完成起来也是有一定的难度的,实验文档都是英文的,并且实验是建立咋了linux平台上的。watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAUlNBLg==,size_20,color_FFFFFF,t_70,g_se,x_16

上图找的有道,可以明白了lab就是有许多题,我们要按照它的要求去具体实现。 先来进行lab1-datalab

一下午的时间还是有收获的,下面我们就一步一步的开始吧!

二、配置lab环境

也看了很多关于配置环境文章,有简单需要安装docker,但对于我们小白,还是找个简单的吧

【CSAPP】lab0 环境的配置_刘_浩的博客-CSDN博客_csapp lab 环境配置【CSAPP】lab0 环境的配置引言我的环境配置折腾了一下午,其实很简单(前提是你已经安装过linux虚拟机)。整体分为三步:1. 下载源码2. 配置gcc环境3. 验证具体步骤1. 下载源码https://blog.csdn.net/qq_45703010/article/details/120897185这个是我找到的最简单的一个搭建环境的方法,这里附上一张我自己配好环境的图watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAUlNBLg==,size_20,color_FFFFFF,t_70,g_se,x_16

 可以看到当我们输入./btest -g 之后已经列出来我现在的lab1的答题情况,包括Score这道题的得分

Rating这道题的难度也是这道题的满分,Errors出现的错误。

注意事项

​ 接下来就是最重要的注意事项,其中分为int题和float题,注意事项不同,主要根据题目上方的提示进行做题。
int题

允许:

    整数常量0到255 (0xFF)。不允许使用像0xffffffff这样的大常量。
    函数参数和局部变量(没有全局变量)。
    一元整型运算!~。
    二进制整数运算& ^ | + << >>。

不允许:

    使用任何控件结构,如if、do、while、for、switch等。
    定义或使用任何宏。
    在此文件中定义任何附加函数。
    调用任何函数。
    使用任何其他操作,如&&,||,-,或?
    使用任何形式的铸造。
    使用除int以外的任何数据类型。这意味着不能使用数组、结构或联合。

float题

​ 对于需要实现浮点运算的问题,编码规则不那么严格。你可以使用循环和条件控制。你可以同时使用整数和无符号。
​ 可以使用任意整数和无符号常量。可以对int或unsigned数据使用任何算术、逻辑或比较操作。
但被明确禁止:

    义或使用任何宏。
    在此文件中定义任何附加函数。
    调用任何函数。
    使用任何形式的铸造。
    使用除int或unsigned以外的任何数据类型。这意味着不能使用数组、结构或联合。
    使用任何浮点数据类型、操作或常量。
 

 三、开摆~

首先我们打开这个文件

ee3e4e5e71414e57928bdcb0e5f82bcd.png

 直接记事本形式打开就可以了,我们直接在里面修改,做题。之后在这个文件夹里打开终端,然后btest检查错误,订正。整体就是这样。我们先来看第一题:
 

1.bitXor

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

用到数学里的公式(参考资料)

2.tmin

 * 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位里有负数,但计算机里的正负是人为来界定的,我们来看下面这张图:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAUlNBLg==,size_20,color_FFFFFF,t_70,g_se,x_16

 0111代表的是7,但是1000就代表了-8,这是四位的图像,32位图像也是一样的。最小值也是10000000000000000000000000000000(一共是32位)

但是注意事项里有提到,我么使用的整数常量只能是0到255 (0xFF),所以我们需要通过位操作来实现得到0x8000 0000。我们将1进行左移位,左移31位后就可以得到。

int tmin(void) {
    int a = 1;
    return a << 31;

 3.isTmax

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

4. allOddBits

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

5.negate


 * 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;
}

6.isAsciiDigit

/* 
 * 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=<0x39,我们将它拆分开,0x30=<x且x=<0x39。即

x-0x30>=0------>(x+~0x30)=a

0x39-x>=0------>(0x39+~x)=b

此时我们只需要判断符号位了,当a,b均大于等于0时,a和b的符号位肯定为0。所以我们分别将a,b右移31位,当a,b均为0时,判断为真。则答案为:

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;

题目要求设计函数来实现x?y:z。首先我们需要知道什么是x?y:z.查找了资料了解了。这是一个三目运算符,这个符号实现下面一个选择分支结构:
if(x)

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。代码如下:

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;
}

实现一个小于等于符号来判断两个数。当x<=y时返回1,否则返回0。转化一下,满足条件y-x>=0时就行。当x,y同号是我们直接向减判断,但是当x,y不同号时,我们需要考虑是否会溢出。

溢出:正-负=正+正。若超过最大正数范围则会正溢出,即正+正=负

           负-正。若超过最小负数范围则会负溢出,即负+负=正

所以需要分情况:1.x,y符号位相同

                             2.x,y符号位不同
太难了。。。借鉴的大佬的:

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;
}

题意是当x不为0时,返回为0,当x为0时,返回为1;像区别正负和0的题我们要先找它的符号位:

首先当x不为0时,我们怎么变换将它转化为0?我们将x和x的相反数进行异或即((~x+1)^x)再进行右移31位,x不为0时得到-1,x为0时我们得到0。最后加1就可以实现函数了。

10.howManyBits

这道题根本看不懂(#-.-)参考的网上:

函数功能:判断x使用补码需要多少位来表示
思路:对于正数,需要寻找的是最后一个为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) {

}

传入函数uf并且返回2*uf,首先我们先看一下浮点数的分类:

                       情况                              对应的数值
规格化的(−1)s×2e−127×1.b22​b21​b20​……b3​b2​b1​b0​
非规格化的(−1)s×2−126×0.b22​b21​b20​……b3​b2​b1​b0​
无穷大和无穷小s=0:+ ∞   s=1:-∞ 
NaNNot a number

 不同类型乘2格式是不一样的,所以我们需要分这四种来分别讨论:

我们定义exp来代表阶码,当uf为规格化时我们只需要exp加1就能实现uf。当uf为非规格化时我们只需要整体左移(这里还是不理解)。当uf为无穷大和无穷小或者NaN时返回它本身。

具体代码如下:
 

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

函数功能:将uf转换为int类型
思路:获取指数域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

 传入x,返回2.0^x,以unsigned代替所要表示的float型。
提取符号位、阶码、尾数,再按上述的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;
}

磕磕绊绊总算完成了,艰难但也收获很多。对一些基础知识更加的熟练。

  • 6
    点赞
  • 56
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值