前言:
1.环境搭建看这篇大佬文章https://zhuanlan.zhihu.com/p/82529114?utm_id=0
2.知识点学习看csapp第二章也可以找视频看(一定要知识点学完再写,逻辑才会清楚,不然会像作者一样巨懵无比)
基本符号
算数运算:
!! 若不为0则得1,为0得0。
! 不为0取非得0,为0取非得1
&& 两个数都不为0得1,只要有0得0
|| 两个数有1得1,都为0得0
位运算:
~ 按位取非
>> 右移
<< 左移
| 按位或
题解
1.bitXor(x,y)
题目要求:只使用两种位运算实现异或操作。
思路:首先理解异或运算,相同为0不同为1,拓展到位运算上也是如此,比较每一位相同或不同;那么我们可以选择~和&符号,取x和y的非再与,最后非运算得到的数中的0为x和y都为0的位;取x和y的与再非,得到的数中的0是x和y都为1的位,所以得到的数中的1为x和y都不同的位。
代码:
/* * bitXor - x^y using only ~ and & * Example: bitXor(4, 5) = 1 * Legal ops: ~ & * Max ops: 14 * Rating: 1 */ int bitXor(int x, int y) { return ~(x&y)&~(~x&~y); }
2.tmin()
题目要求:使用位运算获取2进制补码的最小整数值。
思路:C语言int类型32位,第一位是符号位,最小值为10000...00000,即1左移31位。
代码:
/* * tmin - return minimum two's complement integer * Legal ops: ! ~ & ^ | + << >> * Max ops: 4 * Rating: 1 */ int tmin(void) { return 1<<31; }
3.isTmax(x)
题目要求:通过位运算计算是否是补码最大值。
思路:最大数和最小数之间相差1,最大数+1等于该数取反,但是需要排除-1的情况因为-1加1也等于-1取反(溢出)
代码:
/* * 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) { //最大数和最小数之间相差1,最大数+1等于该数取反,而-1加1也等于-1取反(溢出) return !(~((x+1)+x)|!(x+1)); }
4.allOddBits(x)
题目要求:判断所有奇数位是否都为1。
思路:通过移位来构造特定数,先构造一个奇数位全为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 */ int allOddBits(int x) { //移位可以构造特定数 int allOdd = 0xAA+(0xAA<<8)+(0xAA<<16)+(0xAA<<24); return !((x&allOdd)^allOdd); }
5.negate(x)
题目描述:不使用
-
操作符,求-x
值。
思路:这题就是常识了,不知道的背下来就行。手动推一下也可以,很快的。
代码:
/* * negate - return -x * Example: negate(1) = -1. * Legal ops: ! ~ & ^ | + << >> * Max ops: 5 * Rating: 2 */ int negate(int x) { //取反+1得到相反数 return ~x+1; }
6.isAsciiDigit(x)
题目要求:计算输入值是否是数字 0-9 的
ASCII
值。
思路:移位操作的强大体现在这题,移位可以取连续的位数,移位可以确认正负,首先求出0和9的ascii码这都很基本了,0的ascii码110000,9的ascii码111001,可以看出该数也必须是6位才能在其中,所以需要判断前26位是否为0,其次可以看出,x的前两位为11,最后判断后4位在不在0-9内,可以通过&运算得到后四位,再通过-10来判断范围是不是0-9.
代码:
/* * 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) { //移位可以用来取连续的位数 //移位可以确认正负 //0x30=110000,0x39=111001 //首先判断x前26位是否都为0 int a = x>>6; int condition1 = !a; //其次判断x的前两位是否为11 int b = x>>4; int condition2 = !(b^3); //最后判断后4位在不在0~9以内,用减10来判断 int c = x&(0xF); int condition3 = c+(~0xA+1); int condition4 = condition3>>31; return condition1 & condition2 & condition4; }
7.conditional(x, y, z)
题目要求:使用位级运算实现C语言中的
x?y:z
三目运算符。
思路:根据题目可知,x只要不为0即为真,x为0就是假,那么需要一个式子实现若x不为0则转为1,x为0就是0,最后让这个数加全1就行,若为1则会溢出变全0,为0则全1;y&全1就可以不变而如果&全0则就是0,根据这个规律就能得到代码思路。
代码:
/* * 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) { //用y & ~0得到y,z & ~0得到z //所以需要一个式子实现若x不为0把x转为全1,x为0则转为0 //全1加1等于0 int fx = !x + ~0; return (y&fx)|(z&~fx); }
8.isLessOrEqual(x,y)
题目要求:使用位级运算符实现
<=
思路:这题有坑,要考虑溢出,分四种情况,第一种x==y,可以直接使用异或;第二种,x为+,y为-,x一定大于y,所以不能是这种情况;
第三种:x为-,y为+;第四种:x为-,y为-/x为+,y为+,直接做减法判断符号位;
既然有这种判断所以要先取出符号位(如果有点不懂可以去看作者汇编语言学习笔记中关于溢出的判断方法),分情况讨论就行。
代码:
/* * 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) { //考虑溢出,分四种情况 //第一种,x==y,可以直接用异或 //取符号位 //第二种,x为+,y为-,x一定大于y所以不能是这种情况 //第三种,x为-,y为正 //第四种,x为-,y为-,x为+,y为+,那就直接做减法判断符号位 int condition1 = !(x^y); int signX = (x>>31) & 1; int signY = (y>>31) & 1; int condition2 = (!signX) & signY; int condition3 = signX & (!signY); int res = x+(~y)+1; int condition4 = (res>>31) & 1; return ~condition2 & (condition1 | condition3 | condition4); }
9.logicalNeg(x)
题目要求:使用位级运算求逻辑非
!
思路:这题是符号的变化关键在于利用相反数,若x为0,则x和-x符号位都为0,若x不为0,则x|-x的符号位为1再进行取反就可以
代码:
/* * 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) { //这题是符号的变化关键在于利用相反数 //若x为0,则x和-x符号位都为0,若x不为0,则x|-x的符号位为1再进行取反就可以 return ~(x|(~x+1))>>31 & 1; }
10.howManyBits(x)
题目要求:求值:“一个数用补码表示最少需要几位?”
思路:
首先需要区分正数和负数,正数因为符号位是0,
所以要找最高的存在1的位,最后再加1(符号位)即为所求结果
而负数最高位符号位为1所以不好求,但如果舍弃符号位,
例如111101和101的补码表示的都是同一个值-3,再比如111101和101也是同理
例如1111 111 11 1都是计算机中-1的表示方法,将其转化为原码无非是中间多了几个0,但对结果没有影响
所以负数就要找最高位为0的地方
将负数给取反就把找负数的除符号位外的最高位0转化成了找1的位置,最后不断缩小范围找1的最高位即可
代码:
int howManyBits(int x) { //这题太妙了 //首先需要区分正数和负数,正数因为符号位是0, //所以要找最高的存在1的位,最后再加1(符号位)即为所求结果 //而负数最高位符号位为1所以不好求,但如果舍弃符号位, //例如111101和101的补码表示的都是同一个值-3,再比如111101和101也是同理 //例如1111 111 11 1都是计算机中-1的表示方法,将其转化为原码无非是中间多了几个0,但对结果没有影响 //所以负数就要找最高位为0的地方 //将负数给取反就把找负数的除符号位外的最高位0转化成了找1的位置 int b16,b8,b4,b2,b1,b0; int sign=x>>31; x=(sign&~x)|(~sign&x);//x为正不变,为负则取反,取反负数就会变成找最高位为1 //最高位若为1,右移补1,最高位为0右移补0 // 不断缩小范围 b16 = !!(x>>16)<<4;//高十六位是否有1 x = x>>b16;//如果有(至少需要16位),则将原数右移16位 b8 = !!(x>>8)<<3;//剩余位高8位是否有1 x = x>>b8;//如果有(至少需要16+8=24位),则右移8位 b4 = !!(x>>4)<<2;//同理 x = x>>b4; b2 = !!(x>>2)<<1; x = x>>b2; b1 = !!(x>>1); x = x>>b1; b0 = x; return b16+b8+b4+b2+b1+b0+1;//+1表示加上符号位 }
11.floatScale2(f)
题目要求:求2乘一个浮点数
思路:
该题要懂得浮点数在计算机底层中的表示方法
有规格化,非规格化以及特殊值
尾数frac23位,符号位s1位,阶码expr(指数)8位
再分四种情况讨论,非规格化到规格化浮点数的转化是平滑的,最大非规格化数7/512,最小规格化数8/512
代码:
/* * 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) { //该题要懂得浮点数在计算机底层中的表示方法 //有规格化,非规格化以及特殊值 //尾数frac23位,符号位s1位,阶码expr(指数)8位 //先得到expr,s frac unsigned s = (uf>>31) & (0x1); unsigned expr = (uf>>23) & (0xff); unsigned frac = (uf & 0x7fffff); //uf==0 if(expr == 0 && frac == 0) return uf; //uf==无穷大或无穷小 if(expr == 0xff) return uf; //如果浮点数是非规格化 if(expr == 0){ frac <<= 1; return (s<<31) | frac; } //规格化 expr++; return (s<<31) | (expr<<23) | (frac); }
12.floatFloat2Int(f)
题目要求:将浮点数转换为整数
思路:首先,浮点数能表示的范围大于无符号int的范围,所以如果对于浮点数float来说是负数直接取它的负就可以,还是分四类,先取出s、expr和frac,若为0则返回0,若是最值或NaN返回1<<31,若是非规格化数,数很接近于0所以返回0,若为规格化数,先把尾数23位变为整数,再判断到底幂值是多少,若大于31则溢出小于0则返回0,再根据和23的差调整位数
代码:
/* * 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 s = (uf>>31) & (0x1); unsigned expr = (uf>>23) & (0xff); unsigned frac = (uf & 0x7fffff); //0 if(expr==0 && frac==0)return 0; //最值或非数 if(expr == 0xff)return 1<<31; //非规格化 if(expr==0){ return 0; } //规格化 int ee = expr - 127; frac = frac | (1<<23); if(ee>31) return 1<<31; else if(ee<0){ return 0; } if(ee>=23){ frac<<=(ee-23); }else{ frac>>=(23-ee); } if(s){ return ~frac + 1; } return frac; }
13.floatPower2(x)
题目要求:求 2.0x
思路:由于规格化数尾数前默认有一个1,所以只用求出阶码再移位即可
代码:
/* * 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 */ unsigned floatPower2(int x) { // if(x<-149){ // return 0; // }else if (x<-126){ // int shift = 23+(x+126); // return 1<<shift; // }else if(x<=127){ // int expr = x+127; // return expr << 23; // }else{ // return (0xff) << 23; // } int INF = 0xff<<23; int exp = x + 127; if(exp <= 0) return 0; if(exp >= 255) return INF; return exp << 23; }