任务:按照要求补充13个函数,会限制你能使用的操作及数量

  • bitXor(x,y) 只使用 ~ 和 & 实现 ^
  • tmin() 返回最小补码
  • isTmax(x) 判断是否是补码最大值
  • allOddBits(x) 判断补码所有奇数位是否都是1
  • negate(x) 不使用负号 - 实现 -x
  • isAsciiDigit(x) 判断 x 是否是 ASCII 码
  • conditional(x, y, z) 类似于 C 语言中的 x?y:z
  • isLessOrEqual(x,y) x<=y
  • logicalNeg(x) 计算 !x (不用 ! 运算符)
  • howManyBits(x) 计算表达 x 所需的最少位数
  • floatScale2(uf) 计算 2.0*uf
  • floatFloat2Int(uf) 计算 (int) f
  • floatPower2(x) 计算 2.0的x次方

     
  1. /*
  2. * bitXor - x^y using only ~ and &
  3. * Example: bitXor(4, 5) = 1
  4. * Legal ops: ~ &
  5. * Max ops: 14
  6. * Rating: 1
  7. */
  8. int bitXor(int x, int y) {
  9. return ~((~(x & (~y))) & (~((~x) & y))); // 直接推公式,^可以使用~、&和|表示,而|又可以用~和&表示
  10. }

推导一下公式就可以直接写出来了。

 

  1. /*
  2. * tmin - return minimum two's complement integer
  3. * Legal ops: ! ~ & ^ | + << >>
  4. * Max ops: 4
  5. * Rating: 1
  6. */
  7. int tmin(void) {
  8. int one = 1;
  9. return (one << 31); // 最小的有符号数,符号位为1,其余都是0
  10. }

有符号数是用补码来表示的,Tmin表示最小补码数,对于1个字节大小的补码,最小补码数形式为1000 0000,C语言中int类型占4字节,即32位,所以对1左移31位来构造最小补码。

 

  1. /*
  2. * isTmax - returns 1 if x is the maximum, two's complement number,
  3. * and 0 otherwise
  4. * Legal ops: ! ~ & ^ | +
  5. * Max ops: 10
  6. * Rating: 1
  7. */
  8. int isTmax(int x) {
  9. int neg1;
  10. neg1 = !(~x); // 如果x为-1, 则neg1为1,否则neg1为0,这里是为了排除-1的干扰
  11. return !((~(x+1)^x)|neg1); // 给x加1,再翻转,最后和自身取异或,如果x为Tmax,则返回1,否则返回0
  12. }

函数功能是判断x是否是有符号数的最大值,也就是补码最大值,还是拿1个字节来看,最大补码数的形式为0111 1111,代码中的neg1是为了将-1单独判断出来,因为如果只使用return后面那句(!(~(x+1)^x))的话,会导致当x=-1的时候也会返回1,判断出现错误,而改变后的返回结果可以排除-1的干扰。

 

  1. /*
  2. * allOddBits - return 1 if all odd-numbered bits in word set to 1
  3. * where bits are numbered from 0 (least significant) to 31 (most significant)
  4. * Examples allOddBits(0xFFFFFFFD) = 0, allOddBits(0xAAAAAAAA) = 1
  5. * Legal ops: ! ~ & ^ | + << >>
  6. * Max ops: 12
  7. * Rating: 2
  8. */
  9. int allOddBits(int x) {
  10. int mask = (0xAA << 8) + 0xAA;
  11. mask = (mask << 16) + mask; // 构造掩码
  12. return !((x & mask) ^ mask); // &操作将x的奇数位取出,偶数位置0,之后再与掩码异或判断是否满足条件
  13. }

构造掩码操作即可,将掩码和x进行与操作,可以让x的奇数位置不变,而偶数位置变为0。

 

  1. /*
  2. * negate - return -x
  3. * Example: negate(1) = -1.
  4. * Legal ops: ! ~ & ^ | + << >>
  5. * Max ops: 5
  6. * Rating: 2
  7. */
  8. int negate(int x) {
  9. return ~x + 1; // 补码取相反数操作:按位取反再加一
  10. }

直接套用公式。

 

  1. /*
  2. * isAsciiDigit - return 1 if 0x30 <= x <= 0x39 (ASCII codes for characters '0' to '9')
  3. * Example: isAsciiDigit(0x35) = 1.
  4. * isAsciiDigit(0x3a) = 0.
  5. * isAsciiDigit(0x05) = 0.
  6. * Legal ops: ! ~ & ^ | + << >>
  7. * Max ops: 15
  8. * Rating: 3
  9. */
  10. int isAsciiDigit(int x) {
  11. int negative = 1 << 31;
  12. int lessthan = ~(negative | 0x39); // 构造上界,如果超过,则符号位变为1
  13. int greatthan = (~(0x30) + 1); // 构造下界,如果不足,则符号位变为1
  14. lessthan = negative & (lessthan + x) >> 31;
  15. greatthan = negative & (greatthan + x) >> 31;
  16. return !(lessthan | greatthan); // 判断符号位是否为1
  17. return 2;
  18. }

通过上下界来判断输入的x是否在0x30~0x39的范围中,使用x分别加上界和下界,当x不在这个范围中时,通过判断符号位的变化来得出判断。

 

  1. /*
  2. * conditional - same as x ? y : z
  3. * Example: conditional(2,4,5) = 4
  4. * Legal ops: ! ~ & ^ | + << >>
  5. * Max ops: 16
  6. * Rating: 3
  7. */
  8. int conditional(int x, int y, int z) {
  9. x = !!x; // 判断x是否为0,若x=0,则x赋值为0;若x不为0,则x赋值为1
  10. x = ~x + 1; // 得到x的补码,0的补码还是0,1的补码为-1(二进制序列全1)
  11. return (x&y)|(~x&z); // 若x为0,则返回z;若x为1,则返回y
  12. }

重点在于return语句,这个操作可以根据x的不同来返回不同的值。

 

  1. /*
  2. * isLessOrEqual - if x <= y then return 1, else return 0
  3. * Example: isLessOrEqual(4,5) = 1.
  4. * Legal ops: ! ~ & ^ | + << >>
  5. * Max ops: 24
  6. * Rating: 3
  7. */
  8. int isLessOrEqual(int x, int y) {
  9. int minusx = ~x + 1; // 得到-x
  10. int result = y + minusx; // 得到y - x
  11. int sign = (result >> 31) & 1; // 判断result的符号,如果y>=x,则sign等于0,否则等于1
  12. int xsign = (x >> 31) & 1; // 取出x的符号
  13. int ysign = (y >> 31) & 1; // 取出y的符号
  14. int bitXor = xsign ^ ysign; // 判断x和y符号是否一致
  15. return ((!bitXor)&(!sign)) | ((bitXor&xsign)); // 要么x和y符号相同并且x<=y,要么x和y符号不同并且x<0
  16. }

判断方法:如果x和y同符号,当x<=y则返回1;或者如果x和y不同符号,那么当x<0则返回1;其余情况返回0。

这里根据y-x的结果的符号来判断x和y的大小。

 

  1. /*
  2. * logicalNeg - implement the ! operator, using all of
  3. * the legal operators except !
  4. * Examples: logicalNeg(3) = 0, logicalNeg(0) = 1
  5. * Legal ops: ~ & ^ | + << >>
  6. * Max ops: 12
  7. * Rating: 4
  8. */
  9. int logicalNeg(int x) {
  10. return ((x | (~x + 1)) >> 31) + 1;
  11. }

 

  1. /* howManyBits - return the minimum number of bits required to represent x in
  2. * two's complement
  3. * Examples: howManyBits(12) = 5
  4. * howManyBits(298) = 10
  5. * howManyBits(-5) = 4
  6. * howManyBits(0) = 1
  7. * howManyBits(-1) = 1
  8. * howManyBits(0x80000000) = 32
  9. * Legal ops: ! ~ & ^ | + << >>
  10. * Max ops: 90
  11. * Rating: 4
  12. */
  13. int howManyBits(int x) {
  14. // 原理:对于正数,从高位到低位,找第一个位是1的(比如是n),再加上符号位,则最少需要n+1个位;
  15. // 对于负数,从高位到低位,找第一个位是0的(比如是n),则最少需要n位
  16. int b16, b8, b4, b2, b1, b0; // 表示0~15、16~23、24~27、28~29、30、31的位置处是否含有1,如有,则对其赋值需要的位数
  17. int sign = x >> 31; // 取符号位
  18. x = (sign&~x)|(~sign&x); // 如果x为正则不变,x为负则取反,这里是为了统一正负数,我们之后只用找到含有1的位置即可
  19. b16 = !!(x >> 16) << 4;// 先看高16位是否含有1,若有则表示至少需要16位,所以给b16赋值为16(1 << 4 = 16)
  20. x = x >> b16; // 若有1,则原数右移16位,因为上面已经确定是否至少需要16位(针对0~15);若没有1,则b16为0,x不用移位,继续往下面判断
  21. b8 = !!(x >> 8) << 3; // 看剩余位的高8位是否含有1,若有则表示至少还需要8位,给b8赋值为8
  22. x = x >> b8; // 同理...
  23. b4 = !!(x >> 4) << 2;
  24. x = x >> b4;
  25. b2 = !!(x >> 2) << 1;
  26. x = x >> b2;
  27. b1 = !!(x >> 1);
  28. x = x >> b1;
  29. b0 = x;
  30. return b16+b8+b4+b2+b1+b0+1; // 最后加上符号位
  31. }

注释已经写的很清楚了,可以边看代码边打草稿,很容易理解。

 

  1. /*
  2. * floatScale2 - Return bit-level equivalent of expression 2*f for
  3. * floating point argument f.
  4. * Both the argument and result are passed as unsigned int's, but
  5. * they are to be interpreted as the bit-level representation of
  6. * single-precision floating point values.
  7. * When argument is NaN, return argument
  8. * Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while
  9. * Max ops: 30
  10. * Rating: 4
  11. */
  12. unsigned floatScale2(unsigned uf) {
  13. int exp = (uf&0x7F800000) >> 23; // 取出阶码
  14. int sign = uf&(1 << 31); // 取符号位
  15. if (exp == 0) return uf<<1|sign; // 若为非规格数,直接给uf乘以2后加上符号位即可
  16. if (exp == 255) return uf; // 若为无穷大或者NaN,直接返回自身
  17. exp = exp + 1; // 若uf乘以2(也就是阶码加1)后变成255,则返回无穷大
  18. if (exp == 255) return (0x7F800000|sign);
  19. return (exp << 23)|(uf&0x807FFFFF); // 返回阶码加1后的原符号数
  20. }

需要了解计算机内浮点数的表示方法,了解浮点数中的规格数、非规格数、无穷大和未定义的区别和表示。

我们先看如何表示浮点数:

这里的uf类型为unsigned int,并不是浮点数,但是我们将uf看作为单精度类型,它有32位,最高位是符号位,之后8位保存指数信息,最后23位保存小数信息,所以在代码中我们可以看到,我们通过和0x7F800000取与操作来获得指数信息,再右移23位取出这一部分。

浮点数有几种特殊情况:

1.若exp部分全为0(exp = 0),则是非规格化数,它是一种非常接近0的数;

2.若exp部分全为1(exp = 255),当小数部分全为0时,表示无穷大;当小数部分不为全0时,表示未初始化数据NaN;

3.以上两种情况以外,就是规格化数。

所以我们需要判断uf是哪一种浮点数,并根据它的类型来进行相应的操作。

 

  1. /*
  2. * floatFloat2Int - Return bit-level equivalent of expression (int) f
  3. * for floating point argument f.
  4. * Argument is passed as unsigned int, but
  5. * it is to be interpreted as the bit-level representation of a
  6. * single-precision floating point value.
  7. * Anything out of range (including NaN and infinity) should return
  8. * 0x80000000u.
  9. * Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while
  10. * Max ops: 30
  11. * Rating: 4
  12. */
  13. int floatFloat2Int(unsigned uf) {
  14. int exp = ((uf&0x7F800000) >> 23) - 127; // 计算出指数
  15. int sign = uf >> 31; // 取符号位
  16. int frac = ((uf&0x007FFFFF) | 0x00800000);
  17. if (!(uf&0x7FFFFFFF)) return 0; // 若原浮点数为0,则返回0
  18. if (exp > 31) return 0x80000000; // 若原浮点数指数大于31,返回溢出值
  19. if (exp < 0) return 0; // 若浮点数小于0,则返回0;
  20. if (exp > 23) frac = frac << (exp - 23); // 将小数转化为整数
  21. else frac = frac >> (23 - exp);
  22. if (!((frac >> 31) ^ sign)) return frac; // 判断是否溢出,若符号位没有变化,则没有溢出,返回正确的值
  23. else if (frac >> 31) return 0x80000000; // 原数为正值,现在为负值,返回溢出值
  24. else return ~frac + 1; // 原数为负值,现在为正值,返回相反数
  25. }

需要了解整数和浮点数之间的转化方法,我们要做的就是将浮点数中的指数部分和小数部分取出来,然后通过这两部分来转化为整数,具体操作可以看代码,在这个过程中还要判断是否会产生溢出,以及浮点数是否为规格数等情况,如果产生溢出,我们需要返回一个特定的溢出值。

这里有一个将整数转化为浮点数的例子:

 

  1. /*
  2. * floatPower2 - Return bit-level equivalent of the expression 2.0^x
  3. * (2.0 raised to the power x) for any 32-bit integer x.
  4. *
  5. * The unsigned value that is returned should have the identical bit
  6. * representation as the single-precision floating-point number 2.0^x.
  7. * If the result is too small to be represented as a denorm, return
  8. * 0. If too large, return +INF.
  9. *
  10. * Legal ops: Any integer/unsigned operations incl. ||, &&. Also if, while
  11. * Max ops: 30
  12. * Rating: 4
  13. */
  14. unsigned floatPower2(int x) {
  15. int INF = 0xFF << 23; // 设定一个最大值,也就是阶码位置都为1
  16. int exp = x + 127; // 计算阶码
  17. if (exp <= 0) return 0; // 阶码小于等于0,则返回0
  18. if (exp >= 255) return INF; // 阶码大于等于255,则返回INF
  19. return exp << 23;
  20. }

所有代码及相关实验说明材料都在这里

CSAPP实验——DataLab的更多相关文章

  1. CSAPP:datalab实验记录

    CSAPP:datalab实验记录 bitXor /* * bitXor - x^y using only ~ and & * Example: bitXor(4, 5) = 1 * Lega ...

  2. CSAPP:Lab1 -DataLab 超详解

    写在前面 之前考研的时候csapp的书有刷过5,6遍,所以对书本知识还算比较了解.恰逢最近在学c++的时候,顺带刷一下大名鼎鼎的csapp实验. 0. 环境准备 最好准备一个纯净的Linux系统这里建 ...

  3. CSAPP 之 DataLab 详解

    前言 本篇博客将会剖析 CSAPP - DataLab 各个习题的解题过程,加深对 int.unsigned.float 这几种数据类型的计算机表示方式的理解. DataLab 中包含下表所示的 12 ...

  4. CSAPP实验attacklab

    attacklab 实验报告和答案文件都在 https://github.com/thkkk/attacklab

  5. CSAPP Lab2: Binary Bomb

    著名的CSAPP实验:二进制炸弹 就是通过gdb和反汇编猜测程序意图,共有6关和一个隐藏关卡 只有输入正确的字符串才能过关,否则会程序会bomb终止运行 隐藏关卡需要输入特定字符串方会开启 实验材料下 ...

  6. CSAPP:bomblab

    BOMBLAB实验总结 CSAPP实验BOMB,很头疼,看不懂,勉强做完了. 答案是这样的: Border relations with Canada have never been better. ...

  7. CSAPP缓冲区溢出攻击实验(上)

    CSAPP缓冲区溢出攻击实验(上) 下载实验工具.最新的讲义在这. 网上能找到的实验材料有些旧了,有的地方跟最新的handout对不上.只是没有关系,大体上仅仅是程序名(sendstring)或者參数 ...

  8. CSAPP 六个重要的实验 lab5

    CSAPP  && lab5 实验指导书: http://download.csdn.net/detail/u011368821/7951657 实验材料: http://downlo ...

  9. CS:APP3e 深入理解计算机系统_3e Datalab实验

    由于http://csapp.cs.cmu.edu/并未完全开放实验,很多附加实验做不了,一些环境也没办法搭建,更没有标准答案.做了这个实验的朋友可以和我对对答案:) 实验内容和要求可在http:// ...

随机推荐

  1. Yuchuan_linux_C 编程之八 文件操作相关函数

    一.整体大纲 st_mode整体介绍: st_mode详细介绍: 二. Linux文件操作相关函数 1. stat 作用:获得文件信息,也可以获取文件大小. 头文件 #include <sys/ ...

  2. Python 三程三器的那些事

    装饰器 1.什么是装饰器 装饰器本质是函数,用来给其他函数添加新的功能 特点:不修改调用方式.不修改源代码 2.装饰器的作用 装饰器作用:本质是函数(装饰其他函数)就是为其他函数添加其他功能 装饰器必 ...

  3. Redis面试题集锦(精选)

    1.什么是 Redis?简述它的优缺点? Redis的全称是:Remote Dictionary.Server,本质上是一个Key-Value 类型的内存数据库,很像memcached,整个数据库统统 ...

  4. Java 添加、读取、删除Excel文本框

    本文介绍通过Java程序添加文本框到Excel的方法,添加文本框时,可以添加文本.设置文本方向.文本对齐方式.设置文本框大小.位置.填充色/填充图片.文本框旋转角度.文本框名称.可选文本.文本框隐藏或 ...

  5. Vue2.0 【第二季】第5节 Template制作模板

    目录 Vue2.0 [第二季]第5节 Template制作模板 第5节 Template制作模板 一.直接写在选项里的模板 二.写在template标签里的模板 三.写在script标签里的模板 Vu ...

  6. 使用form 表单 弹出登录框,只传递数据,不刷新界面

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  7. pytorch的自动求导机制 - 计算图的建立

    一.计算图简介 在pytorch的官网上,可以看到一个简单的计算图示意图, 如下. import torchfrom torch.autograd import Variable x = Variab ...

  8. 【JAVA进阶架构师指南】之三:深入了解类加载机制

    前言   在上一篇文章中,我们知道了JVM的内存划分,其中在说到方法区的时候说到方法区中存放的信息包括[已被JVM加载的类信息,常量,静态变量,即时编译的代码等],整个方法区其实就和类加载有关. 类加 ...

  9. 【python 数据结构】相同某个字段值的所有数据(整理成数组包字典的形式)

    class MonitoredKeywordMore(APIView): def post(self, request): try: # 设置原生命令并且请求数据 parents_asin = str ...

  10. hdu4107Gangster 线段树

    题目链接:http://icpc.njust.edu.cn/Problem/Hdu/4107/ 题目给定一个初始值都是零的序列,操作只有一种,就是给一个区间加上一个数,但是当一个数大于等于给定的P的时 ...