CSAPP Data Lab
Posted joker D888
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CSAPP Data Lab相关的知识,希望对你有一定的参考价值。
CS:APP Data Lab
前言
data lab主要考察位运算,补码,浮点数等相关内容,此lab能检验对整型,浮点数存储方式的理解,以及位运算的各种妙用,能够对整数浮点数的实际使用的过程中,对可能出现的“怪异”结果知其所以然。
以下13个函数即是本lab需要完成的内容,根据函数上方的注释及可用操作符限制补充完函数即可,部分函数不允许任何超过8bit的常量,详细内容查看相关注释。
lab资源下载点这里。
对于一些可能用到或了解的知识在最下方列出。
lab
-
bitXor
/* * bitXor - x^y using only ~ and & * Example: bitXor(4, 5) = 1 * Legal ops: ~ & * Max ops: 14 * Rating: 1 */ // 求x^y // 思路,先按位与求出x和y同为1的位,然后x,y分别和~c按位与,求出各自不同时为1的位a,b // 最后分别对a,b取反,使得各自位上同时为0或同时为1的位设为1,再将两者按位与后再取反,即得出不同时为1的位 int bitXor(int x, int y) // x:4 y:5 int c = x & y; // 0100 int a = x & ~c; // 0000 int b = y & ~c; // 0001 int ret = ~a & ~b; // 1110 return ~ret; // 0001 // 或,不是同时为0和不是同时为1的两种情况进行按位与 // return ~(~x & ~y) & ~(x & y);
-
tmin
/* * tmin - return minimum two's complement integer * Legal ops: ! ~ & ^ | + << >> * Max ops: 4 * Rating: 1 */ // 求Tmin // 将1左移31位,这样除了最高符号位其余全为0,即可取得最小值 int tmin(void) return 1 << 31;
-
isTmax
/* * isTmax - returns 1 if x is the maximum, two's complement number, * and 0 otherwise * Legal ops: ! ~ & ^ | + * Max ops: 10 * Rating: 1 */ // 判断x是否是Tmax // 若x是Tmax,那么x+1=Tmin,而~Tmin=Tmax,所以可以以此验证,此外当x=-1时,由于溢出也满足这个等式,还需要特判一下 // 利用异或和逻辑非来判断等式是否成立,!!(x+1)来判断x是否为-1,!!是将数字转化为true或false int isTmax(int x) return !(~(x + 1) ^ x) & !!(x + 1);
-
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 */ // 求x的二进制奇数位上全为1 // 如果奇数位上全为1,返回true int allOddBits(int x) int a, b; a = 0xAA + (0xAA << 8); // 注意运算符优先级 a= a + (a << 16); // a的奇数位全为1,偶数位为0 b = a & x; // 如果x的奇数位上也全位1,等式的结果为a return !(a ^ b); // 用异或和逻辑非判断两者是否相等
-
negate
/* * negate - return -x * Example: negate(1) = -1. * Legal ops: ! ~ & ^ | + << >> * Max ops: 5 * Rating: 2 */ // 求x的相反数 // 补码的非=补码取反+1 , -x=~x+1 int negate(int x) return ~x + 1;
-
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 */ // 求x是否在0x30~0x39之间 // 即 00110000~00111001之间,发现4~5位固定是1,之后全0, // 而0~3位则从0000递增变化到1001,所以我们先判断高位的24个bit是否为0x3(a) // 再判断第3位是否为1(b),若为1,则判断1,2位上是否有1(c),利用b和c的按位与即可判断 int isAsciiDigit(int x) int a = !(x >> 4 ^ 3); // 高位的24个bit是否为0x3 int b = !!(x & 8); // 第3位是否为1 int c = !!(x & 6); // 则判断1,2位上是否有1 return a & !(b & c); // 若高24bit为0x3,且低4位不超过0x9,return 1
-
conditional
/* * conditional - same as x ? y : z * Example: conditional(2,4,5) = 4 * Legal ops: ! ~ & ^ | + << >> * Max ops: 16 * Rating: 3 */ // 模拟三元运算符 // 使用!x-1,即可将x的真假翻转,-1用~0来实现 int conditional(int x, int y, int z) int a = !x + ~0; // 若x是0,a=0,否则a是全1 return (a & y) | (~a & z);
-
isLessOrEqual
/* * isLessOrEqual - if x <= y then return 1, else return 0 * Example: isLessOrEqual(4,5) = 1. * Legal ops: ! ~ & ^ | + << >> * Max ops: 24 * Rating: 3 */ // 模拟<= // if x<=y ,返回1,否则返回0 // 通过y-x的正负即可判断,但作差可能导致溢出,需要特殊考虑一下 int isLessOrEqual(int x, int y) int sign_x = x >> 31 & 1; // x的符号位 int sign_y = y >> 31 & 1; // y的符号位 int a = y + (~x + 1); // y - x int sign_a = a >> 31 & 1; // a的符号位 // 若a最高位是0,即y-x>=0,应该返回1,但若x>0,y<0,那此时是负溢出造成的结果,应当返回0 // 若a最高位是1,即y-x<0,应该返回0,但若x<0,y>0,那此时是正溢出造成的结果,应当返回1 // 总上,返回1有两种可能,x,y符号相同且a最高位是0,以及x<0,y>0(符号位无所谓,x负y正,返回1即可) int nosame_xy = sign_x ^ sign_y; // 若x,y符号位不同为则为1 return (!sign_a & !nosame_xy) | (nosame_xy & sign_x);
-
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 */ // 实现逻辑非,0返回1,非0返回0 // 只有0的相反数是其本身,所以x^(-x) 只有当x为0时,结果为0,否则,结果是负数,最高位为1, // 同时需要注意,在补码中,-Tmin=Tmin,需要特判 // 所以将最高位提取出来与1异或即可 // 如果最高位是1,返回0,如果最高位是0,且x^(-x)的最高位是1,返回0 int logicalNeg(int x) int sign_x = (x >> 31 & 1) ^ 1; // 如果符号位是1,等式结果为0,否则为1 int sign_xor = ((~x + 1) ^ x) >> 31 & 1; //提出 x^(-x)的最高位 return sign_x & (sign_xor ^ 1); // ^1 是为了翻转最低位 // 或,上面的用了10个ops,显然冗余了一些,简化如下 // 对于(~x+1)>>31,若x是正数,结果为-1,若x是负数数或0,结果是0,也就不用x^(-x)来判断了,但也需要特判 // 用 x|(~x+1>>31) 来表示,若结果为-1,则x不为0,若结果为0,则x为0。最后加上1返回即可 // return ((x | (~x + 1)) >> 31) + 1;
-
howManyBits
/* howManyBits - return the minimum number of bits required to represent x in * two's complement * Examples: howManyBits(12) = 5 * howManyBits(298) = 10 * howManyBits(-5) = 4 * howManyBits(0) = 1 * howManyBits(-1) = 1 * howManyBits(0x80000000) = 32 * Legal ops: ! ~ & ^ | + << >> * Max ops: 90 * Rating: 4 */ // 求x用二进制补码表示最少需要几位 // 若x为正数,显然求出其最左边的1的处于第几位,再加1表示符号位即可 // 若x为负数,则求出其最左边的0的位置,再加1表示符号位即可 // 为啥负数是找最左边的0呢,因为最高位符号位的权值,如11011(-5)而,1011(-5) // 可以发现,假设有w+1位,若符号位的紧邻右边的位也是1的话,先不管其余位,那么值为-(2^w)+(2^(w-1))=-(2^(w-1)) // 也就是说,与其1100...00,不如100...00这样表示,能使得位数更小,符号位紧邻右边有连续的1同理 // 我们不妨对x去反,这样就可以和正数一样找最左边的1 // 那么怎么找最左边的1呢,二分思想,一开始我们先看原x的高16位是否有1,若有则将x>>16,相当于砍掉x的低16位 // 若没有,则说明高16位全位0,不动,相当于砍掉x的高16位,以此8,4,2进行下去 int howManyBits(int x) int b16,b8,b4,b2,b1,b0; int sign= x >> 31; // 若x是正数不变,若x是负数,按位取反,| 左边是为了应对负数,|右边是为了应对正数,各自的另一半则为0 x = (sign & ~x) | (~sign & x); // 二分思想 b16 = !!(x >> 16) << 4; //若高16位有1,二进制位的4位置为1,权值2^4=16 x = x >> b16; // 若有,此表示式将x右移16位,否则不变 b8 = !!(x >> 8) << 3; // 同理,高8位是否有1 x = x >> b8; b4 = !!(x >> 4) << 2; // 高4位是否有1 x = x >> b4; b2 = !!(x >> 2) << 1; // 高2位是否有1 x = x >> b2; b1 = !!(x >> 1); // 最1位是否有1 x = x >> b1; // 上面所谓的高几位是原x不断砍半后的二进制位,b0为了检验移位到最后剩下的两个bit位的最低位是否为1 b0 = x; return b16 + b8 + b4 + b2 + b1 + b0 + 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 */ // 计算2*f,f是浮点数,不过参数和返回值都是无符号整形表示 // 首先,对于NaN,不用计算,直接返回,NaN的阶码部分全位1,尾数部分不为0, // 先求阶码exp=(uf0x7f800000)>>23 ,再求尾数 frac=uf&0x7fffff // 如果exp=255,并且尾数非0,那就是NaN,return uf即可,如果frac全0,表示无穷大,*2还是无穷大,也return uf即可 // 如果exp=0,非规格化形式,若frac=0,表示值为0,若frac!=0,表最接近0的那些数,可以统一处理为尾数*2,即frac<<=1,不用担心 // 如果exp!=0 && exp!=255,规格化形式,只需要把exp+1,即可 // 曾想过,为什么非规格化且不为0的时候为什么不能是exp+1,毕竟uf*2,不就是阶码+1,但非规格化和规格化一样处理的话 // exp+1后,就从非规格化变为了规格化,但float最小的非规格化为1.4*e-45,最小的规格化为1.2*e-38, // 显然比较小的非规格化按这种方式处理后,一跃变为了规格化,变大了不止两倍。 unsigned floatScale2(unsigned uf) unsigned exp = (uf & 0x7f800000) >> 23; unsigned frac = uf & 0x7fffff; unsigned sign = uf >> 31 & 1; unsigned res; if(exp == 0xff) return uf; else if(exp == 0) frac <<= 1; res = sign << 31 | frac; // 本应还要与上exp<<23,但此时exp全0就每必要了 else ++exp; res = sign << 31 | exp << 23 | frac; return res;
-
floatFloat2Int
/* * 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型 // 如果原浮点数值为0,return 0, // 若指数大于31,溢出,返回0x80000000 // 若指数小于0,返回0 // 否则,把小数部分看为整数(即相当于小数部分整体左移了23位),再根据exp的大小,对小数部分进行最终调整 // 若exp>23,还得左移exp-23,若exp<23,说明左移多了,还得右移23-exp位 // 最后根据符号位进行正负调整,返回即可 // int floatFloat2Int(unsigned uf) int sign = uf >> 31 & 1; // 取出符号位 int exp = ((uf & 0x7f800000) >> 23) - 127; // 指数 int frac = (uf & 0x007fffff) | 0x00800000; // 小数,且加上了1 // 若uf为0或指数小于0,返回0 if(!(uf & 0x7fffffff) || exp<0) return 0; // 若指数大于31,溢出 if(exp >= 31) return 0x80000000; else if(exp > 23) // 右移少了 frac <<= exp - 23; else // 右移多了 frac >>= 23-exp; if(sign) frac = -frac; return frac;
-
floatPower2
/* * floatPower2 - Return bit-level equivalent of the expression 2.0^x * (2.0 raised to the power x) for any 32-bit integer x. * * The unsigned value that is returned should have the identical bit * representation as the single-precision floating-point number 2.0^x. * If the result is too small to be represented as a denorm, return * 0. If too large, return +INF. * * Legal ops: Any integer/unsigned operations incl. ||, &&. Also if, while * Max ops: 30 * Rating: 4 */ // 计算2.0^x // 浮点数标准用V=(-1)^s * M * 2^E // 根据浮点数表示,x就是exp部分 // 根据int的范围进行讨论即可 // 单精度exp占8位,阶码E=e-Bias(2^7-1) ,即范围是[-126,127] // 若 x>=128,超过所能表示的最大值了,+INF // 若 >=-127, 在指数表示范围内,直接放入exp部分 // 若 x>=-150,exp部分能放127,frac有23位,根据标点数frac的含义,也可以用来表示,所以只要不小于150,都可以表示出来 // 若 x<150, 超出最小的表示范围了,return 0 unsigned floatPower2(int x) if(x >= 128) return 0x7f800000; if(x >= -126) return (x + 127) << 23; if(x >= -150) return 1 << (x+150); return 0;
上方所有函数经测试结果如下
note
以上是关于CSAPP Data Lab的主要内容,如果未能解决你的问题,请参考以下文章