Q1 bitxor
//1
/*
- bitXor - x^y using only ~ and &
- Example: bitXor(4, 5) = 1
- Legal ops: ~ &
- Max ops: 14
- Rating: 1
*/
分析:题目要求的是通过用取反~和&运算符实现异或运算,在网上找到了异或运算的真值表
然后通过本学期学的离散数学知识通过真值表将x^y使用与,或,非即对二进制进行取反,步骤如下
x^y = (x ∨ y) ∧ (~x ∨ ~y) (第一行和第四行的和之积表达式)
= (x ∧ ~y) ∧ ~(x ∧ y) (德摩根律)
转换后再用c语言进行表达如下
int bitXor(int x, int y) {
int Xor = (x & ~y) & ~(x & y);
return Xor;
}
Q2 tmin
/*
- tmin - return minimum two’s complement integer
- Legal ops: ! ~ & ^ | + << >>
- Max ops: 4
- Rating: 1
*/
这道题目要求使用! ~ & ^ | + << >>运算符来实现最小补码值,而最小的补码值是1000 0000 0000 0000 0000 0000 0000 0000,也就是将1向左移位31位到符号位也就是第32位即可,代码如下
int tmin(void) {
return 1 << 31;
}
Q3 isTmax
//2
/*
- isTmax - returns 1 if x is the maximum, two’s complement number,
-
and 0 otherwise
- Legal ops: ! ~ & ^ | +
- Max ops: 10
- Rating: 1
*/
此题是判断x是不是最大值
而最大值加一则是二进制的最小值,但如果想证实一个数一定是Tmax,那么可以借助Tmax + 1 = Tmin来确定,因为它们是互为充要条件的。
Tmax + 1 = Tmin 可以推出Tmax - Tmin + 1 = 0,而通过Tmax - Tmin + 1 = 0又可以推出Tmax + (-Tmin) + 1 = 0,最后推出max + Tmin +1=0,但是要排除x=-1的这种情况,代码如下
int isTmax(int x) {
int i = x + 1;
x = x + i;
x = -x;
i = !i;
x = x + i;
return !x;
}
Q4 allOddBits
此题目是要求判断奇数位上是否全是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
*/
这道题的关键在于找出掩码值0xAAAAAAAA,这个数字很特别,它保留了所有奇数位,而此道题的解题思路是先构造出这样一个掩码值然后使其与x相与来获取x奇数位的对应情况,接着通过与掩码值异或来判断x对应奇数位是否全为1,而此类题往往先找特殊掩码在进行判断,代码如下
int allOddBits(int x) {
int i = (0xAA << 8) | 0xAA;(首先获取0xAAAAAAAA作为此问题的掩码, 而且0xAA作为常数值是可以直接使用的)
int j = (i << 16) | i;
return !((x & j) ^ j);
}
Q5 negate
这道题要求的是求相反
/*
- negate - return -x
- Example: negate(1) = -1.
- Legal ops: ! ~ & ^ | + << >>
- Max ops: 5
- Rating: 2
*/
对任何补码,-a = (~a + 1),逆元的位的快速求解就是按位取反再加1,也就是根据-a = (~a + 1)得到:-x=~x+1,代码如下
int negate(int x) {
return (~x + 1);
}
Q6 isAsciiDigit
此题是要判断x是否是大于0x30且小于0x39的数
//3
/*
- 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
*/
将满足条件的数字的二进制位全部列出来,如下所示:
0011 0000
0011 0001
0011 0010
0011 0011
0011 0100
0011 0101
0011 0110
0011 0111
0011 1000
0011 1001
其他范围内的数值第3位均为0,且0~2位包含了所有可能的情况。这意味着它们可以被归并为一类,而将剩下的两个作为特例来处理,变量j、k就分别验证了0x38、0x39这两个特例的情况,剩下的就是将原值x右移三位,此时应该确保剩下来的值是0x6(110),这对应了剩下的情况,最终返回值为三个条件的或,意思是满足了其中一条即可。
代码如下
int isAsciiDigit(int x) {
int j = !(x ^ 0x38); /特殊值0x38/
int k = !(x ^ 0x39); /特殊值0x39/
x = !((x >> 3) ^ 0x6);
return x | j | k;
}
Q7 conditional
此题要求进行x?y:z的运算
/*
- conditional - same as x ? y : z
- Example: conditional(2,4,5) = 4
- Legal ops: ! ~ & ^ | + << >>
- Max ops: 16
- Rating: 3
*/
判断x为0或非0,使用!,得到1或0,+~0得到全0和全1,以此为掩码,代码如下:
int conditional(int x, int y, int z) {
int a = !x+~0; //x==0,a=0000;x!=0,a=1111
return (a&y)|(~a&z);
}
Q8 isLessOrEqual
/*
- isLessOrEqual - if x <= y then return 1, else return 0
- Example: isLessOrEqual(4,5) = 1.
- Legal ops: ! ~ & ^ | + << >>
- Max ops: 24
- Rating: 3
*/
判断0 < = y − x 0<=y-x0<=y−x,只用判断符号位是否为0,又− x = ∼ + 1 -x=\sim+1−x=∼+1,所以y − x = y + ∼ x + 1 = a y-x=y+\sim x+1= ay−x=y+∼x+1=a,判断a的符号位,为0返回1,但需要排除异号相减的情况,代码实现如下:
int isLessOrEqual(int x, int y) {
int s1 = x>>31;
int s2 = y>>31;
int s = s1^s2;
int a = y+~x+1;
int b = !(a>>31);
return (!!(s&s1))|((!s)&b);
}
Q9 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
*/
当x!=0时,返回1,此时分为x<0和x>0两种情况:
当x<0,此时x的符号位为1;而当x>0,此时x不为0,取补+1的符号位不为0
代码实现如下:
int logicalNeg(int x) {
int a = ~(x>>31); (x<0, a=0;a=1)
int b = ((~x+1)>>31)+1; (x>=0, x!=0, a=0; a=1)
return a&b;
}
Q10 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
*/
这道题没有做出了在网上找的思路:要搞清楚一个数补码最少需要多少位,必须得找出来正数中位于最高位的1在哪里,这一点很好理解。如果是负数,就要找负数中最高位的0在哪里,然后再+1(表示符号位),可以这样理解负数的负权重全部是由最高符号位贡献的,越大的负数越容易使用较小的位数表示,如果我们想表示更小的负数就必须增加位数,同时保证符号位为1,在负数的二进制表示中出现的最高位0是一种标志,意思是这里的正权重不用了,那么也可以确定表示此数无需更多的二进制位
所找的代码如下
int howManyBits(int x) {
/获取数字的符号位, 如果符号位为1,那么Sign也是1/
int Sign = x >> 31;
/如果是负数那么按位取反,如果是正数那么保持原值/
x = (Sign & ~x) | (~Sign & x);
/接下来就是寻找数字中最高位的1在哪里/
/*
以第一个式子为例,如果判断高16为仍有1存在,
那么低16位必须保留,不用考虑它们了,直接移掉
*/
int High16 = !!(x >> 16) << 4;
x = x >> High16;
int High8 = !!(x >> 8) << 3;
x = x >> High8;
int High4 = !!(x >> 4) << 2;
x = x >> High4;
int High2 = !!(x >> 2) << 1;
x = x >> High2;
int High1 = !!(x >> 1);
x = x >> High1;
int High0 = x;
return High16 + High8 + High4 + High2 + High1 + High0 + 1;
}
Q11 float_twice
/*
- float_twice - 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
*/
此题思路就是分类讨论分为规划数字和非规划数字
1.当数字是非规格化数字时,只需要将Frac部分左移一位即可,如果有进位,那么就实现了从非规格化数到规格化数的平滑过渡。
2.当数字是规格化数字时,因为Frac部分需要加上一个隐式的1,所以单凭左移一位无法获得对应的2倍关系,这时候只需要直接在Exp部分+1即可,但是要排除无穷大的情况,无穷大返回自身即可。NaN直接在进行运算之前直接剔除掉。
代码如下
unsigned float_twice(unsigned uf) {
unsigned f = ~(uf|0x807fffff);
if (f) {
unsigned a = uf&0x7f800000;
if (a) {
unsigned u = uf+0x800000;
if (~(u|0x807fffff)) {
return u;
}
return u&0xff800000;
}
return (uf&0x80000000)|(uf<<1);
}
return uf;
}