第一题
用 &与~ 来完成 '异或运算符' 的功能
好像跟数学有关系 德摩根定律 搞半天
非(P 且 Q) = (非 P) 或 (非 Q)//离散数学等值演算
NAND = ~(P & Q) = ~P | ~Q // 就是这个 直接用这个公式反推一下就可以了
//这个公式有点难理解(数学逻辑运算) 直接背算了
NOR = ~(P | Q) = ~P & ~Q
非(P 或 Q) = (非 P) 且 (非 Q)
异或运算符 长这样 中间不能用或 然后再用 德摩根定律套用就可以
//a⊕b = (¬a ∧ b) ∨ (a ∧¬b)
/*
* 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));
}
推导过程如图
58113)
第二题
求整型数补码所表示的最小值
注意实现这道题所用到的常数不能超过8bit
32位的虚拟机 说明 最多有 1000...
补码 的公式 第一位是-2^w次方 其他位都是正的 那么我想要最小值 第一位为一 其他为零就好了 然后再左移补零就可以
答案 : 我拿1 左移31位就可以 ps : 只要我的最低位为一 我就可以通过左移达成目的 同理 “3” 左移31位 应该也是可以的
3.isTmax
判断x是不是补码表示的最大值
审题 :
补码表示 最高位 必须为零 然后其他位是一就可以
32位 用16进制表示 0x00000000 八个零 每位是4个二进制位 每一位都是占用4bit 1 byte是8 bit
同样我们判断 只需要知道 我们有前提 0x7FFFFFFF 是最大值 此时最高位为零 其他都为一
当我们对其+1 时 最高位为1 其他位是零 刚刚好 与原来的值相反 通过这一点来判断是不是最大值
其他值加一 都做不到和原值相反 因为只有最大值+1才会改变最高位
另外 : 全为1的oxFFFFFFFF +1 会丢失一位 导致变成全为零 要排除这种情况
int isTmax(int x) {
int tmin = x+1;
int equal = tmin^(~x);
return (!!tmin)&(!equal);
}
计算机内部用补码表示 然后 自己写一下源码就能明白 16进制和二进制转换真的很简单
4.allOddBits
如果x中的所有奇数位都为1,则返回1,不是则返回0 , 注意 奇数位是从左往右数为奇数的
内部都是二进制来表示 并且是补码表示
有个位运算是"与" 表示 相同则为1 只要是和奇数位全为1的数进行逻辑与运算那就一定与其相同
所以答案就是
因为不能 直接赋值 8bit 就是2个16进制位 因为4个二进制位为一个16进制位
然后给填充的零里面加1 用 ^ 只要不同则为1 可以将奇数位赋值
int allOddBits(int x) {
int a_8 = 0xAA;
int a_16 = (a_8 << 8)|(a_8);
int a_32 = (a_16 << 16)|(a_16);
int equal = (x & a_32) ^ a_32;
return !equal;
}
5.negate
返回x的相反数
很简单 在底层 整数就是二进制补码 相反数就是 按位取反+1
为什么呢 : 因为正数的补码是本身 // 例如 -3补码转换成3的补码
负数的转为原码是按位取反+1(符号位不变)
而一个负数的补码要转换成相反数的补码 // 负数的源码跟他的正数源码只差了符号位
其相反数的补码和他的原码是相同的 //本来符号位不要取反 现在全都取反再补一
6.isAsciiDigit
判断 0x30 <= x <= 0x39,是返回1,不是则0
我们移项可知 可通过判断正负来判断范围
向右移31位我们可以得到符号位
int isAsciiDigit(int x) {
return !((x + ~0x30)>>31) | ((0x39 + ~x)>>31));
}
7.conditional
实现 x ? y : z (x不为0返回y,为0返回z)
就是三目运算符
int conditional(int x, int y, int z) {
x = ~(!!x) + 1; 当x为非0时把他变成0xFFFFFFFF 这个值很特殊 每个位都是1 //和他进行&运算 会保留自己所有的位
·····
当x=0时 每个位变成全零 此时返回 z 否则返回y 就是 return(x&y|~x&z)
8.isLessOrEqual
实现一个小于等于符号来判断两个数。当x<=y时返回1,否则返回0。转化一下,满足条件y-x>=0时就行。当x,y同号是我们直接向减判断,但是当x,y不同号时,我们需要考虑是否会溢出
减法
* Legal ops: ! ~ & ^ | + << >>
没有减法 用前面的
y-x怎么跟零比 也就是和前面一样 通过右移看符号位
这里不能用if语句所以我们通过位运算 和!来完成0 和 1 的输出
当然防止 正+正=负的情况 我们可以 情况进行排除 只要都为正 直接return 1
负+负=正的情况 我们可以 只要都为负 我们就return 0
先写 return 1 的 (y>>31)&(x>>31)| 同为1 时为真 此时说明大家都是负数 为零时待定
(y>>31)^(x>>31) 同号为0 异号为1
若为同号 只要判断 一个东西的正负
return ()|()
9.logicalNeg
**实现!运算符 只要不是全零 我就返回 0 **
0是全零
通过相反数来解 最后只看符号位 通过前面的公式来找到相反数
int t=(~x+1)
int c=(t^x)>>31
我们将他的符号位变成第一位 而且是逻辑右移 前面补得的最高位 移动后 0:00000000 非0: 1111111111 也就是-1
return c+1
10.howManyBits
**函数功能:判断x使用补码需要多少位来表示 看不懂了属于是 **
/* 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 flag;
int cnt_16,cnt_8,cnt_4,cnt_2,cnt_1,cnt_0;
int sign = x>>31;
x = (sign&(~x))|(~sign&(x));
flag = !!(x>>16);
cnt_16 = flag<<4;
x = x>>cnt_16;
//b16 = !!(x>>16)<<4;
//x = x>>b16;
flag = !!(x>>8);
cnt_8 = flag<<3;
x = x>>cnt_8;
flag = !!(x>>4);
cnt_4 = flag<<2;
x = x>>cnt_4;
flag = !!(x>>2);
cnt_2 = flag<<1;
x = x>>cnt_2;
flag = !!(x>>1);
cnt_1 = flag;
x = x>>cnt_1;
cnt_0 = x;
return cnt_16 + cnt_8 + cnt_4 + cnt_2 + cnt_1 + cnt_0 + 1;
}