实验报告
part1 解题思路
1.bitxor(实现异或)
容易想到,bitxor的定义就是(x&~y)|(~x&y)
,再根据离散数学学过的德摩根律,
~(a|b)=(~a)&(~b)
,带入即可转化为~((~(x&~y))&(~(~x&y)))
,很不错做出来了,是用了8个操作符;
不过想想这么一坨看起来很累赘,换个思路,我们要使(x&~y)|(~x&y)
成立,其实可以否定另外两种情况———x&y
和~x&~y
,于是改进为这样的代码:~(~x&~y)&~(x&y)
,这样用了7个操作符(好吧也没差多少
2.tmin(最小补码数)
根据定义是1000000…所以1<<31
,没什么好说的。
3.isTmax(是不是最大补码数)
最大补码数长这样:01111111…,本来想直接取反跟Tmin异或来着,发现不让用<<。。。
换个思路,发现Tmax加一后取反为自己,这样的数只有Tmax和111111111111(-1),于是写出了这样的代码
!!
是很用的,可以把一切非0值转换为1:
int j = x + 1;
return !(x ^ ~j) & !!j;
意思就是x^~j为全零且j不为0(x不为-1)
进一步优化代码,把!提出来,得到
int j = x + 1;
return !((x ^ ~j) | !j);
一共6个操作符。
4.allOddBits(奇数位为全一)
意思就是问输入的是不是1x1x1x1x1x1x…1x1x这样的数,x位置随便。
这个运算比较像|
,那么构造01010101010...10101
与x
进行|
,如果得到11111111111
(-1)就ok了,构造这样的代码:
int tmp = 0x55 + (0x55 << 8);
tmp += tmp << 16;//tmp应该是01010101...010101
return !((tmp | x) + 1);
5.negate(相反数)
根据数字逻辑知识,取反加一就可以得到相反数,return ~x+1
;
6.isAsciiDigit(是不是ascii里的0~9)
要判断'0'<=x<='9'
,只需要与对应值做差,再看符号位就行了。
那么应该是x-'0'
符号位为0且x-'9'-1
符号位为1;代码这样:
int b = ~(0x3A) + 1;//b为-('9'+1)
return (!((x + 10 + b) >> 31)) & ((x + b) >> 31);
7.conditional(模拟三元运算符)
代码如下:
int conditional(int x, int y, int z) {
x = !!x;
x = (~x) + 1;
return (x & y) | (~x & z);
}
首先把x转换为非零的1或者0,考虑用1111以及0000这样的掩码对输出处理,想到对x取反加一可以得到-1(111111…1111)和0(0000000…00000),那这就非常的好了。
8.isLessOrEqual(x<=y ?)
思路其实很简单,判断y-x的符号位即可,但仔细一想,要考虑到溢出问题,所以再用符号位判断一下。如果符号位不同,那么可以直接得出答案;如果符号位相同,那么一定不会溢出,由y-x符号位得出答案。
敲代码的时候发现不考虑溢出居然也能过,看来是linux这个环境的问题(?),跟同学讨论发现居然真有人忘了,de了一圈bug。。看来是会有人忘考虑溢出的,请多多hack一下(不是)
放代码:
int isLessOrEqual(int x, int y) {
int xsign, ysign, signsame;//x符号,y符号,符号是否同
xsign = x >> 31;
ysign = y >> 31;
signsame = xsign ^ ysign;//same is 0
return ((!ysign) & xsign) | !(signsame | ((y + ~x + 1) >> 31));
}
解释一下,如果xsign为0,ysign为1,那么((!ysign) & xsign)
会得到0,反之xsign为1,ysign为0,可以返回1。
signsame
保证了如果x,y符号不同,后面的算式!(signsame | ((y + ~x + 1) >> 31)
一定会返回0,不会干扰前面的判断。如果相同,则signsame为0,结果取决于y-x符号位。
9.logicalNeg(表示!
)
要实现0输出1,其他输出0,0有什么特点呢?取反加一还为零。我是偶然想到了取反加一,这样除了0和1000…000,其他互为都为相反数。(x | (~x + 1)) >> 31
可以使一切非零的数得到11111111…111,而0会得到0,那么只需要再加一就可以实现逻辑!
了。最终为
return ((x | (~x + 1)) >> 31) + 1;
10.howManyBits(用补码表示会要多少位?)
这题是做的有点笨的,先找一波规律:
注意考虑符号位
由于我们实际上做的是对负数取反但不加1,对正数不变,因此构建sign=x>>31
,以及那么x = x ^ sign;
就可以使负数取反而正数不变。这是关于负数的处理,下面来看代码
int howManyBits(int x) {
int sign, x16, x8, x4, x2, x1;
sign = x >> 31;
x = x ^ sign;
x16 = (!!(x >> 16)) << 4;
x = x >> x16;
x8 = (!!(x >> 8)) << 3;
x = x >> x8;
x4 = (!!(x >> 4)) << 2;
x = x >> x4;
x2 = (!!(x >> 2)) << 1;
x = x >> x2;
x1 = x >> 1;
x = x >> x1;
return x16 + x8 + x4 + x2 + x1 + x + 1;
}
思路采用二分,以这个为例
x16 = (!!(x >> 16)) << 4;//x16判断第16位及更高是否有1,如果有的话x16=16.
x = x >> x16;//右移x16位,这样x16是否成立不会干扰接下来的判断。
就这样一直分分分,直到把一个两位数分为x1,剩余x必定为0或1。
最终结果就是x16 + x8 + x4 + x2 + x1 + x + 1
,注意符号位所以加了一个1,美美收工。
11.floatScale2(2*uf=?)
先贴代码:
unsigned floatScale2(unsigned uf) {
int exp, s;
s = uf & 0x80000000;
exp = uf & 0x7f800000;
if (exp == 0)
return (uf << 1) + s;
if (exp == 0x7F800000)
return uf;
exp += 0x800000;
if (exp == 0x7F800000)
return exp + s;
return exp + (uf & 0x807fffff);
}
思路就是先处理得到s和exp,s得到了1000000…或者000000…
exp = uf & 0x7f800000;
把除了exp位置的地方都置为0
如果exp==0
,那么直接return (uf << 1) + s;
就算由非规格化小数变为规格化也是正确的,不得不说浮点数的设计十分巧妙。
如果exp==0x7F800000
也就是实际上exp为265,那么直接返回原来的值uf就可以。
现在让exp+=0x800000
也就相当于加一,如果为265则溢出,返回exp+s
(无穷),否则返回exp + (uf & 0x807fffff)
,就这样。
12.floatFloat2Int(浮点变补码)
代码:
int floatFloat2Int(unsigned uf) {
int s,E,M;//这次是对应位置的s,E,M
s=uf>>31;
E=((uf&0x7f800000)>>23)-127;//真实值
M=(uf&0x7fffff)|0x00800000;//真实值(已经加一)
if(E>30)//需要把1.___往左移动31位及以上,必定溢出。
return 0x80000000;
if(E<0)// 真实值为1.___居然还敢右移,直接给你返回0
return 0;
if(E<23)//如果E<23那么实际上M应该右移
M>>=(23-E);
else//反之左移
M<<=(E-23);
if(s)//如果符号位是1(负数)
return -M;
else//反之
return M;
}
如果是非规格数,那么在这个函数会返回0,并没有问题。
感觉写的挺明白的,直接看就好了