直入主题,13道bit运算puzzle,其中收获最大的是howManyBits
和floatFloat2Int
- 用~和&实现^异或。
思路:可以分成四种情况,(1,0), (1,1), (0,0), (0,1),我们发现,需要将位上不相同的数保留为1,相同的变成0,而&操作是两个数同为1时才是1,否则是0,于是对于不相同的数,我们想到了先取&,然后取反~,自然都会变成1,对于相同的数,如果执行同样操作,那么只有(1,1)会变成0,而(0,0)会变成1,但我们希望他们都变成0,所以我们想到了把两个结果用&组合一下,但是如果保持同样操作不变地组合,(0,0)的结果还是1,所以我们可以对于其中一边先同时取反,这样可以保证两个相同的数做操作,总有一边是0,另一边是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) {
return ~(~x & ~y) & ~(x & y)
}
- int 4字节,TMin最高位1,其余为0,没啥好说的
/*
* tmin - return minimum two's complement integer
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 4
* Rating: 1
*/
int tmin(void) {
return 1 << 31
}
- 判断是否TMax。其实,当且仅当x=TMax或-1时,才有x+1=~x。知道这个就好算了,先判断是否为TMax or -1,只有这两种情况异或为0,然后再除去-1(也就是1111)的情况,这里要知道,只有0000取非!是1,其他值取非都是0
/*
* 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) {
//x = TMax or -1
int x1 = x+1;
int x2 = ~x;
int xor = x1^x2; //0000
//see if x = -1
int y1 = ~x; //0000
int res1 = !y1; //1
return !(xor | res1);
}
- 判断一个数是否在奇数位上全是1。可构建mask 10101010...,然后按位&看结果是否与mask相等。由于不能用==判断,我们可以把结果与mask做异或然后取反。需要注意的是,lab1限制定义int为1字节,所以不能直接写int mask = 0xAAAAAAAA,需要做一些左移操作
/*
* 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 a = 0xAA;
int b = (a << 8) + a;
int mask = (b << 16) + b;
int and = x & mask; //should == mask
return !(and ^ mask);
}
- 取负 = 取反 + 1,书上写了
/*
* negate - return -x
* Example: negate(1) = -1.
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 5
* Rating: 2
*/
int negate(int x) {
return ~x + 1;
}
- 首先判断高4位是否为0011, 再判断低四位是否在0000和1001之间。可以看到,如果低4位以0开头,则后3位可为任意值;如果以1开头,那么接下来两位也必须是0,可写为
/*
* 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) {
int left = !((x >> 4) ^ 3); //1 if 0x3.
int r1 = (x & 2) >> 1;
int r2 = (x & 4) >> 2;
int r3 = (x & 8) >> 3;
int right1 = !r2 & !r1; //r3 = 1
int right2 = !r3; //r3 = 0
int right = right1 | right2;
return left & right;
}
- 如果x=0,return z,其他情况return y。
思路: 可知最终结果要么是y要么是z,所以他俩必须出现在同一个表达式中,而且需一个数与1111做&,另一个数与0000做&。剩下的就是怎么把1或0转成1111和0000了
/*
* 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) {
int flag = !!x; //if x != 0, 1, else 0
int f = ~flag + 1; //convert to 1111 or 0000
return (f & y) | (~f & z);
}
- 如果x<=y, return 1, else return 0。主要就是分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) {
//cond 1, same sign, x <= y
int xhighest = (x >> 31) & 1;
int yhighest = (y >> 31) & 1;
int samesign = !(xhighest ^ yhighest);
int diff = ~x + 1 + y;
int xlessy = !((diff >> 31) & 1);
int res1 = samesign & xlessy;
//cond 2, not same sign, x < 0
int xlesszero = xhighest;
int res2 = (!samesign) & xlesszero;
return res1 | res2;
}
9.实现logicalNeg,!0=1, !Others=0。
思路:参考了一下别人的,除了0,其他数其补码(取反+1)再和自己做或(|)操作得出的最高位始终为1。0做同样操作最高位为0,然后右移31位+1就可。注意,是+1,因为0+1=1,-1+1=0
/*
* 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) { //!0 = 1, !others=0
return ((x | (~x+1)) >> 31) + 1;
}
- 输出最少需要的位数来表示int x
思路:这道题太好玩了!解题思路我们分两步
首先不考虑负数情况,当x>=0时:
假设我们有个32位int,二进制表示为 00010100 11011111 01001010 00001100,要想看到我们最少需要多少位数,我们发现当x为正数的时候,最高位符号位为0,那么我们只需要找到从最高位往后数第一个1所处的那个位置,然后再加上1就是了。
这里巧妙的思路就是运用二分查找!我们先看高16位是否有1,具体怎么判断呢?把x右移16位,然后取反再取反,如果是1证明最高16位有1,是0则没有。如果有1,表明我们至少需要低16位的数,把这个结果记下来。然后我们需要把x右移,那么右移多少位呢?答案是高16位如果有1,我们右移16位,如果没有1,则不右移。
接下来继续看右移后剩下的数的最高8位!以此类推,最后把得到的结果加起来!
那么,负数我们怎么算呢?通过观察我们发现,除了最高位符号位是1,负数需要的最高位数是从最高位往后数第一个0所处的位置,为什么呢?举个例子,比如1101,整数值为-3,也可以表示成101 (-4 + 1 = -3),所以实际上我们只需要3位就可以表示它;而对于1010,整数值为-6,则不能缩减位数。通过类似列举我们发现,可以看做从最高位往后开始找到第一个0的位置,就是对于负数需要的位数。这个时候,我们只需要把这个值给他取反~,就相当于对于正数我们从最高位开始找第一个1的过程了。
是不是脑洞大开!
/* 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
*/
int howManyBits(int x) {
int f16, f8, f4, f2, f1, f0, r16, r8, r4, r2, r1, r0;
int mask=x>>31; // x < 0, 111...1; x>=0, 000...0
x = (mask & ~x)|(~mask & x); //if x < 0, only need to find highest 0, which is equal to use ~ then find the highest 1
//binary search!
f16 = !!(x >> 16); //see if high 16 has 1s
r16 = f16 << 4; //16 or 0
x = x >> r16;
f8 = !!(x >> 8); //see if left high 8 has 1s
r8 = f8 << 3; //8 or 0
x = x >> r8;
f4 = !!(x >> 4); //...
r4 = f4 << 2; //4 or 0
x = x >> r4;
f2 = !!(x >> 2);
r2 = f2 << 1; //2 or 0
x = x >> r2;
f1 = !!(x >> 1);
r1 = f1; //1 or 0
x = x >> r1;
f0 = x;
r0 = f0;
return r16 + r8 + r4 + r2 + r1 + r0 + 1; //1 means sign bit
}
11.float x 2。
思路:可以参考书上的浮点数表达式 v = S * M * 2^E,分为M全为1的情况,全为0的情况,及一般情况。
全为1时,乘2必溢出,为NaN
全为0时,M=0.f,乘2需不需要进位决定于小数位第一位是否为1(和是否>=0.5)
其他情况为规格浮点数,只需要将E位+1就可
/*
* 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) {
int s = (uf >> 31) & 1;
int e = (uf >> 23) & 255;
int m = uf & 0x7FFFFF;
int a;
if (e == 255) {//infinite
return uf;
}
if (e == 0)
{
m = m << 1; //x2
a = m >> 23; //to see if origin m >= 0.5, then *2 should >=1
if (a == 1) {
e = e + 1;
}
return (s << 31) | (e << 23) | m;
}
else {
e = e + 1;
}
return (s << 31) | (e << 23) | m;
}
- float转int
思路:这道题写起来还是需要一定的思考的,还是分3种情况
e全为1,返回给定值
e全为0时,返回0
规格浮点数时,需要先把小数部分转成整数,也就是res = (1 << 23) | m,其实这个值的意思也是当E = e - Bias = 23时(单精度浮点数小数部分为23位),返回的整数值。于是可以分为当E>23时,相当于给res乘以2的E-23次方,于是我们可以用while循环来操作,这里还需要判断一下如果res溢出的情况。当E<23时,相当于乘以1/2的23-E次方,这道题可以多思考一下,会对浮点数转整数有很好的理解
/*
* 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) {
int s = (uf >> 31) & 1;
int e = (uf >> 23) & 255;
int m = uf & 0x7FFFFF;
if (e == 255) {
return 0x80000000u;
} else if (e == 0) {
return 0;
} else {
int res = (1 << 23) | m; //res if E = 23
int E = e - 127; //e - bias
if (E > 23) {
while (E > 23) {
E--;
res = res << 1;
if (res < 0) {
//E can be up to 128, so could be overflow
return 0x80000000u;
}
}
} else {
while (E < 23) {
E++;
res = res >> 1;
}
}
if (s == 1) {
res = -res;
}
return res;
}
}
- 返回2的x次方的浮点数表示
思路,熟悉浮点数的表达公式就比较容易想,S和M都为零,只看2的x次方就好。从表达式里可以看到x=e-127,所以e=x+127,而浮点数中e需要属于0到255之间,则可得到答案
/*
* 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) {
int e = x + 127;
if (e < 0) {
return 0;
} else if (e > 255) {
return 0x7f800000;
} else {
return e << 23;
}
}
附上最终的结果:
Correctness Results Perf Results
Points Rating Errors Points Ops Puzzle
1 1 0 2 7 bitXor
1 1 0 2 1 tmin
1 1 0 2 7 isTmax
2 2 0 2 7 allOddBits
2 2 0 2 2 negate
3 3 0 2 15 isAsciiDigit
3 3 0 2 8 conditional
3 3 0 2 16 isLessOrEqual
4 4 0 2 5 logicalNeg
4 4 0 2 36 howManyBits
4 4 0 2 16 floatScale2
4 4 0 2 20 floatFloat2Int
4 4 0 2 4 floatPower2
Score = 62/62 [36/36 Corr + 26/26 Perf] (144 total operators)
lab2见!