Datalab做题笔记
0x1 bitXor
只用~
和&
表示^
分析:(横着的是a,竖着的是b)
XOR
1 | 0 | |
---|---|---|
1 | 0 | 1 |
0 | 1 | 0 |
令 ~A = a&~b
1 | 0 | |
---|---|---|
1 | 0 | 0 |
0 | 1 | 0 |
~B = ~a&b
1 | 0 | |
---|---|---|
1 | 0 | 1 |
0 | 0 | 0 |
则A&B有
1 | 0 | |
---|---|---|
1 | 1 | 0 |
0 | 0 | 1 |
再对A&B取~得到XOR
即XOR = ((a&(~b) ) & ((a) & b) )
0x2 tmin
输出int的最小值
解析: 补码Tmin最高位为1,其他位为0。int有32bit,则Tmin = 1 <<31
0x3 istmax
* isTmax - returns 1 if x is the maximum,
* two's complement number,
* and 0 otherwise
* Legal ops: ! ~ & ^ | +
* Max ops: 10
* Rating: 1
解析:Tmax除了最高位是0,剩下的31bit全为1
操作符可以用 ! 那么想办法把Tmax凑成0就可以了
则
- (Tmax + 1) = (10000.....)
- Tmax + Tmin = -1
- ~(-1) = 0
- 也就是当x = Tmax, ~ ((x + 1) + x) = 0
- 主要到对于0xffffffff也成立, 特判即可
整理得到
ans = !((~((x + 1) + (x))) | (!(x + 1)))
0x4 allOddBits
Legal ops: ! ~ & ^ | + << >>
题意:判断奇数比特是否是1
例如 (大端机器)A = 1010(从左向右) 奇数位全是1,题目要求最多12个操作符。所以不能一个个判断。
-
假如我们已经有了Book = 0xAAAAAAAA
-
那么y = x&Book,奇数位是1,偶数位全是0。
-
~Book + y 如果奇数位全是1,那么此时32个比特上全是1。再取反,符合条件的全是0
-
得出ans = !(Book + x & Book)
0x5 negate
用位运算计算-x
解析: -x = ~x + 1
0x6 isAsciiDigit
判断是否为ASCII数字
解析: 在前一个小问,已经可以做出减法。只要判断是否在0和9之间即可。 注意边界处理
还有一个要注意的点就是,如果用第一个bit判断正负,0的第一位和正数第一位都是0,需要先减去1,这样就可以区分开了。
int isAsciiDigit(int x)
{
int minusX = (~x) + 1;
int m = (minusX + 0x30 -1 ) >> 31; // -
int n = (0x39 + minusX ) >> 31;//+
return (m)&(!n);
}
0x7 conditional
return x ? y : z
思路: 如果x非0,那么返回y,否则返回z。
先j = y ^ z, 再XOR得到结果
考虑cnt = !x - 1
x | 真 | 假 | 操作 |
---|---|---|---|
cnt | 0xffffffff | 0 | |
y | 0 | 1(需要XOR) | |
z | 1 | 0 |
int conditional(int x, int y, int z)
{
int cnt = !x - 1;
return (y ^ z) ^ (y & (~cnt)) ^ (z & (cnt));
}
0x8 isLessOrEqual
判断是否x小于等于y?
return x <=y
x <= y
y - x >=0
但是int加减可能造成溢出。
进行优化:
如果符号相同,那么正数大于负数。
如果符号相反,那么判断差值。
int isLessOrEqual(int x,int y)
{
int signX = x >> 31, signY = y >> 31;
int conditionOne = (signX & 1) & (!(signY | 0));
int minusX = (~x) + 1;
int conditionTwo = (y + minusX) >> 31;
return conditionOne | ( (!((!(signX|0))&(signY&1)))&(!conditionOne) &(!conditionTwo));
}
貌似写的很丑陋,符号用的太多了
0x9 logicalNeg
使用位运算计算x。
解析:也就是符号为全是0,返回值为1。否则为0。注意到负数的首个bit一定为1,正数的相反数首个bit也是1(补码原理)。那么0呢?0再怎么取相反数,首位都是0。嘻嘻
注意,就是int范围并不是对称的,-2147483648取反溢出等于-1
int logicalNeg(int x)
{
return (x| (((~x) + 1) >> 31)) + 1;
}
0xA howManyBits
题意: 判断一个数字至少用多少bit的补码。
分析: 比如-5,因为$-5 = -2^3 + 2 + 1$可以用补码$(2)1011$一共四位表示。而$9 = 2 ^ 3 + 1$不难发现规律是向下取幂。对于正数,是找最高位的1,对于负数,是找最高位的10再上一位。取反就是01再上一位。
x = x(负数) ? (-x) : x
前面写过三目运算符了。
x = (~x&(x >> 31) ) | (x&(~(x>>31)))
这样x如果是负数就被取反了。现在我们需要找到最高位的1在哪个地方。
x = x | (x >>2);
x = x | (x >>4);
x = x | (x >>8);
x = x | (x >>16);
因为这样最高位的1一定能够填充右边的所有bit,所以再统计有多少个1。最后cnt+1就行辣。
这道题最多可以用90个操作符,我们当然可以一个个bit统计,但是这样太猪鼻了。
让我们想想,如果前16位有至少一个1,那么至少有16个1;
if(前16位有1)
f(统计前16位1的个数)
else
f(统计后16位1的个数)
!!(x>>16 & (-1))
如果结果是1,则前16位为1。
x = x >> (16 & -flag)
表示右移选择
举个例子,当最后还剩2位时,只有11,10,00这三种情况。
前两种右移1位,得到1,后一种不用右移,还是0。所以最后还要加右移完的x。
int howManyBits(int x)
{
x = (~x&(x >> 31) ) | (x&(~(x>>31)));
int cnt16,cnt8,cnt4,cnt2,cnt1;
cnt16 = (!!((x>>16)&(-1))) << 4;
x = x >> cnt16;//remain 16bit
cnt8 = (!!((x>>8)&(-1))) << 3;
x = x >> cnt8;
cnt4 = (!!((x>>4)&(-1))) << 2;
x = x >> cnt4;//remain 4bit
cnt2 = (!!((x>>2)&(-1))) << 1;
x = x >> cnt2;//remain2bit
cnt1 = (!!((x>>1)&(-1)));
x = x >> cnt1;
x = cnt16+cnt8+cnt4+cnt2+cnt1 + x + 1;
return x;
}
0xB floatScale2
输出一个小数的2倍,这个小数以unsigned int的bit来表示
unsigned floatScale2(unsigned uf)
{
unsigned sign = uf >> 31;
unsigned exp = (uf >> 23 )& 0xff;
unsigned f = uf & 0x007fffff;
if(exp == (1 << 8) -1 )
{
return uf;
}
if(!exp)
{
return (sign<<31 )|(exp<<23)|(f<<1);
}
else
return (sign<<31 )|((exp+1)<<23)|(f);
return 2;
}
0xC floatFloat2Int
0xD floatPower2
题意:参数x, 返回2.0^x
分析:
- 非规格化,exp为0,偏码后为-126,frac为$2(-23)*1$,最小为$2(-149)$,所以当$x<-149$,
return 0
- 当frac为2(-1)*,乘以*2(-126),所以非规格化最多表示$2^(-127)$ 当$-149<=x<=-127$ ,比如x = -139 的时候,前面已经乘以2(-126)*,后面再乘以*2(-13)就好了,也就是$frac = 2^(x + 126)$第$x + 126 + 24$位(从右向左)是1,
return 1<<(x + 126 + 24 -1)
也即 return 1<<(x + 149) - 考虑规格化数字 $1<=exp<=254$,此时减去偏码127,得到$2^(-126~127)$,所以当$-126<=x<=127$ 返回 $(127 + x) << 23$
- 考虑特殊值,当x >= 255,exp需要全为1,返回inf就可以了
总结
终于把datalab做完了,头疼。