写本文章的初衷,一个是本着分享的精神,在做实验的过程中记录下自己哇哦哇哦的知识点,另一个也是一种对自己的督促,毕竟自己做总是没什么动力,来过看过觉得不错的,评论个催更,也算是给我个学习动力。
0. 本节内容
在这次实验中,我们在实现功能的同时,而摒弃使用for,if等控制结构(部分),目的是深刻掌握位运算,打下坚实的基础
1. bitXor
实验描述
/*
* 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 | 0 | 1 |
|---|---|---|
| 0 | 0 | 1 |
| 1 | 1 | 0 |
本题难点其实在于操作符号的限制上,由上表易得
但这里不能使用|因此不妨利用两次取反有
int bitXor(int x, int y) {
return ~((~(~x&y))&(~(x&~y)));
}
2. tmin
实验描述
/*
* tmin - return minimum two's complement integer
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 4
* Rating: 1
*/
返回INT_MIN
思路
直接使用移位操作符
int tmin(void) {
return 1 << 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
*/
判断是否参数x是否为int最大值
思路
INTMAX在硬件中表示为除符号位均为1,以四位为例,我们可以发现0111+1 = 1000,这也是0111的反码,在提交后我们发现 -1,即1111也具备该特征(由于溢出),因此我们要考虑去除-1,即x+1 == ~x && x != -1时,return 1 否则返回0。
但实验要求我们不能使用这些操作符,因此我们需要使用 ! ~ & ^ | +分别表示==,&&,!=。
这里可以使用之前学到的,异或,来表示是否等于, 由于返回值只有1和0,因此可以用位运算$代替‘且’,而x != -1也就是x + 1 != 0——> !(x + 1 == 0)——> !(x + 1),有
int isTmax(int x) {
return !((x + 1)^(~x)) & !!(x+1);
}
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
*/
如果所有奇数位为1,则return 1 ,否则返回0 注意:从0开始计数
思路
判断奇数位是否均为1最简单的方法是将x与0xAAAAAAAA异或运算判断是否相等,这个之前已经讲过了
int allOddBits(int x) {
int t = 0xAAAAAAAA;
return !((t & x) ^ t);
}
5. negate
实验要求
/*
* negate - return -x
* Example: negate(1) = -1.
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 5
* Rating: 2
*/
返回相反数
思路
这个学过计组的都应该知道,取反加1,不知道的推荐看下CSAPP,国内很多教材又是补码又是反码的其实讲的很乱,
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
*/
如果x的硬件表示0x30 <= x <= 0x39,则返回1,否则返回0
思路
分步做,先看高4位是否为3,判断相等用异或,再看低4位是否小于等于9,我们通过为0x9取反,并加上低4位,如果低4位大于9,此时应该会发生溢出。【 这部分知识点要听了CSAPP才好想出来,因为课中讲了为什么设计取反加一来表示负数 】
int isAsciiDigit(int x) {
int pre = x >> 4 ^ 0x3;
int d = x & 0xF;
int c = ~0x9+d;
int b = 0x80000000;
return !pre & !((c & b) ^ b);
}
7. conditional
实验要求
/*
* conditional - same as x ? y : z
* Example: conditional(2,4,5) = 4
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 16
* Rating: 3
*/
如果x 为 true【非0】 return y 否则 return z
思路
看起来就是x && y || !x && z,然而操作码要求替代||,&&
,注意,之前使用&直接代替&&是因为数字只有一位,现在需要用和yz等长的0或1进行位运算了
int conditional(int x, int y, int z) {
int a = 0xffffffff;
return (~(a + !!x) & y) + ((a+!!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
*/
如果 x <= y return 1, 否则 return 0
思路
这道题很容易想到 x - y <= 0,或者发现≤不会表示,机智的想到了y - x > 0,而这道题的坑其实是要考虑溢出问题,那么不妨考虑减法什么时候会发生溢出,即只有负数减一个正数的时候,正数减负数时才会出现溢出。其中但此时又可以明显看出两者大小关系,因此我们使用符号位来代替溢出时的状况
同时,我们可以通过移位操作来判断符号位
int isLessOrEqual(int x, int y) {
int a = x >> 31;
int b = y >> 31;
// x + y -
int c = a& ~b;
int d = ~a& b;
return (c & 1) |(~c & ~d & !(!!((~x+1+y) >> 31)));
}
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
*/
参数为0则return 1否则return 0
思路
- 正数的相反数是负数,负数的相反数是正数,只有0的相反数是自己
- 正数和0的操作码都是0,但0的操作码等于自己
多个思路,自己写个
10. 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可由几位表示,需要注意的是: 需要符号位, 负数前几个都是1的位数不算在内
思路
这道题一般我们都是用for循环来处理,但这里不允许,甚至不可以用if,所以使用了一个神乎奇技的二分查找 [二分查找其实没什么难的,但每次出现我都妙啊妙啊]
int howManyBits(int x) {
int flag = x >> 31 ;
int t = (~flag & x) | (flag & ~x );
// er fen
int b16 = !!(t >> 16) << 4;
t = t >> b16;
int b8 = !!(t >> 8) << 3;
t = t >> b8;
int b4 = !!(t >> 4) << 2;
t = t >> b4;
int b2 = !!(t >> 2) << 1;
t = t >> b2;
int b = !!(t >> 1);
t = t >> b;
return b16 + b8 + b4 + b2 + b + t + 1;//+sign
}
11. 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
*/
return 2 f
思路
做这题之前需要补充下浮点数知识点
1.IEEE754
| 规格 | S | E | M |
|---|---|---|---|
| 单精度 | 1 | 8 | 23 |
| 双精度 | 1 | 11 | 52 |
| 临时浮点数 | 1 | 15 | 64 |
2.规格化
-
阶码(exp)不全为0或全为1
-
E=Exp-Bias
-
Bias:
-
通过这种方式保证Exp为无符号数,方便比较大小
-
-
-
M:1.xxxx 隐藏1
- Min: frac=000 (M=1.0)
- Max:frac=111(M=2.0-)
-
特殊值
| exp | frac | 值 |
|---|---|---|
| 111 | 000 | |
| 111 | 非000 | NaN() |
| 000 | 当作小数处理 | 非规格化:E=1-bias |
综上,除了简单的exp+1的场景外,考虑几个特殊值即可
unsigned floatScale2(unsigned uf) {
int exp = (uf & 0x7f800000) >> 23;
int sign = uf >> 31 &0x1;
int frac = uf & 0x7FFFFF;
// exp 111
if (! (exp ^ 0xFF))return uf;
// exp 000
if(! (exp ^ 0)){
frac <<= 1;
return sign << 31| exp << 23 | frac;
}
return sign << 31| (exp + 1)<< 23 | frac;
}
12. 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
*/
将float转变为int
思路
这里通过类似验码加移位的操作方式得到Exp,frac,sign的值,其中sign由于在第一位,右移会时符号位一起移动,所以再次用验码相交。
这次主要考虑如下几点
- e在0 到 31之间,正常计算,其中如果e < 23,无法将所有的frac化为正数,要舍去23-e位,否则将frac左移e-23位
- e小于0,则返回0,e大于31或出现其他特殊值,则返回 0x80000000u.
- 考虑sign
int floatFloat2Int(unsigned uf) {
int exp = (uf & 0x7f800000) >> 23; //fuhao
int sign = uf >> 31 &0x1;
int frac = uf & 0x7FFFFF;
int e = exp - 127;
if(e < 0){
return 0;
}
else if(exp == 0xFF|| e >= 31) return 0x80000000u;
frac = 1<<23 | frac;
if(e < 23){
frac = frac >> 23-e;
}else{
frac = frac << e-23;
}
if(sign == 0){
return frac ;
}return -frac;
}
13. 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
*/
返回
思路 同样需要对x的范围进行讨论,如前文所述,exp取值范围位127 , -126,而如果x小于-126,则需要用frac来代替,但最少为-148
unsigned floatPower2(int x) {
// if(x < -126){return 0;
int t;
if(x < -148){
t = 0;
}else if(x > 127){
t = 0xFF << 23;
}else if(x <= 127 && x >= -126){
t = x + 127 << 23;
}else{
t = 1 << x + 148;
}
return t;
}