知识储备
首先需要明白基本的一些操作符所代表的含义
按位与(&)、按位或(|)、按位异或(^)、按位加(+)、逻辑取反(!)、按位取反(~)、左移(<<)、右移(>>)
按位与运算将两个运算分量的对应位按位遵照以下规则进行计算:
0 & 0 = 0, 0 & 1 = 0, 1 & 0 = 0, 1 & 1 = 1。
按位或运算将两个运算分量的对应位按位遵照以下规则进行计算:
0 | 0 = 0, 0 | 1 = 1, 1 | 0 = 1, 1 | 1 = 1
按位异或运算将两个运算分量的对应位按位遵照以下规则进行计算:
0 ^ 0 = 0, 0 ^1 = 1, 1 ^ 0 = 1, 1 ^ 1 = 0
按位加运算将两个运算分量的对应为按位遵照以下规则计算:
0 + 0 = 0,0 + 1 = 1,1 + 0 = 1,1 + 1 = 0
左移运算将一个位串信息向左移指定的位,右端空出的位用0补充,每左移1位相当于乘2
右移运算将一个位串信息向右移指定的位,右端移出的位的信息被丢弃,每右移1位,相当于除以2。对无符号数据,右移时,左端空出的位用0补充。对于带符号的数据,如果移位前符号位为0(正数),则左端也是用0补充;如果移位前符号位为1(负数),则左端用0或用1补充,取决于计算机系统。对于负数右移,称用0补充的系统为“逻辑右移”,用1补充的系统为“算术右移”。
按位取反为二进制串对应的1和0进行变换
逻辑取反只有0和非0两种情况
/*
* lsbZero - set 0 to the least significant bit of x
* Example: lsbZero(0x87654321) = 0x87654320
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 5
* Rating: 1
*/
int lsbZero(int x) {
//x右移一位再左移一位实现把最低一位有效位置0
x=x>>1;
x=x<<1;
return x;
}
/*
* byteNot - bit-inversion to byte n from word x
* Bytes numbered from 0 (LSB) to 3 (MSB)
* Examples: getByteNot(0x12345678,1) = 0x1234A978
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 6
* Rating: 2
*/
int byteNot(int x, int n) {
int y=0xff;
n=n<<3;
y=y<<n;
x=x^y;
return x;
}
补充说明一道题:x怎么取第n个字节
/*
* getByte - Extract byte n from word x
* Bytes numbered from 0 (LSB) to 3 (MSB)
* Examples: getByte(0x12345678,1) = 0x56
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 6
* Rating: 2 (x>>8*n)&0xff
*/
int getByte(int x, int n) {
return ((x>>(n<<3))&0xff);
}
题意解析:
对于0x12345678
当n=0时,return 0x78
当n=1时,return 0x56
当n=2时,return 0x34
当n=3时,return 0x12
解答过程与思考:
首先,在这个过程中,x应先右移8* n位再取最低的8位数值,
即 return (x>>8* n)&0xff
但是 不能用* 所以采用移位
return ((x>>(n<<3))&0xff);
/*
* byteXor - compare the nth byte of x and y,
* if it is same, return 0, if not, return 1
* example: byteXor(0x12345678, 0x87654321, 1) = 1
* byteXor(0x12345678, 0x87344321, 2) = 0
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 20
* Rating: 2
*/
int byteXor(int x, int y, int n) {
n=n<<3;
x=(x>>n)&0xff;
y=(y>>n)&0xff;
return !!(x^y);
}
&&表示当两个操作数都是ture的时候,才返回true
/*
* logicalAnd - x && y
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 20
* Rating: 3
*/
int logicalAnd(int x, int y) {
x=!((!x)|(!y));
//x=(!(!x)&(!(!y);
return x;
}
设计思想:
1.把x和y分别取NOT,二者相或后再取NOT,即可得到逻辑与。
2.把x和y分别转换为逻辑的0和1,再相与。
|| 表示当两个操作数有一个为ture的时候,就返回true
/*
* logicalOr - x || y
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 20
* Rating: 3
*/
int logicalOr(int x, int y) {
x=(!(!x))|(!(!y));
return x;
}
把x和y分别转换为逻辑的0和1,再相或。
补充一道题:逻辑右移位(默认为算术右移位)
* logicalShift - shift x to the right by n, using a logical shift
* Can assume that 0 <= n <= 31
* Examples: logicalShift(0x87654321,4) = 0x08765432
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 20
* Rating: 3
*/
int logicalShift(int x, int n) {
int mask=~(((1<<31)>>n)<<1);
return (x>>n)&mask;
}
若x为正数或0,
0x0xxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx
return x>>n即可。
若x为负数,1xxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx
位运算时带符号整数 符号拓展前面将会补充1, 例如:
取(0x-87654321,4)
1000 7 6 5 4 3 2 1 右移4位,则1111 1000 7 6 5 4 3 2,后32-n位没问题
只需添加一个掩码,掩码把符号位转为0,若右移n位,则前n位应为0
1的原码为0000 0000 0000 0000 0000 0000 0000 0001
1<<31变成 1000 0000 0000 0000 0000 0000 0000 0000
右移(n-1)位,掩码为1111 0000 0000 0000 0000 0000 0000 0000
取反为 0000 1111 1111 1111 1111 1111 1111 1111
和x>>n进行与得到结果
由于可允许的运算符号 *Legal ops: ! ~ & ^ | + << >>*没有减号,所以将函数写成:
int logicalShift(int x, int n) {
int mask=~(((1<<31)>>n)<<1);
return (x>>n)&mask;
}
/*
* rotateLeft - Rotate x to the left by n
* Can assume that 0 <= n <= 31
* Examples: rotateLeft(0x87654321,4) = 0x76543218
* Legal ops: ~ & ^ | + << >> !
* Max ops: 25
* Rating: 3
*/
int rotateLeft(int x, int n) {
int y;
y=~((~0)<<n);
x=(x<<n)+((x>>(32+(~n+1)))&y);
return x;
}
设计思想:先构造y为高(32-n)位为0的y,再与x右移(32-n)的x相与,相当于储存了x的高n位数,最后再与x左移n位相加即可。
方法一 参照上一道补充题,右移32-n位,将函数写成
int rotateLeft(int x, int n) {
int mask=~(((1<<31)>>31)<<n);
return (((x>>32)<<n)&mask)+(x<<n);
}
报错,right shift count >= width of type,x>>32时右移计数>=类型的宽度
修改,x>>(32-n),取n的补码,等于源码取反加一
x=(x<
方法二 想构造掩码为1111 1111 1111 1111 1111 1111 1111 0000,还可让 y=~(( ~0)<
int logicalShift(int x, int n) {
int mask=~((~0)<<(32+(~n+1)));
return (x>>n)&mask;
}
/*
* parityCheck - returns 1 if x contains an odd number of 1's
* Examples: parityCheck(5) = 0, parityCheck(7) = 1
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 20
* Rating: 4
*/
int parityCheck(int x) {
x=x^(x<<16);
x=x^(x<<8);
x=x^(x<<4);
x=x^(x<<2);
x=x^(x<<1);
x=x>>31;
return !(!x);
}
设计思想:将x的前16位与后16位进行按位异或……
每次移位将x的低半位数与高半位数进行异或,实现得到的x位表示总是减去偶数个1,并不影响x位表示中1个数的奇偶性。最后把得到的x右移31位,并变成逻辑1和0.
/*
* mul2OK - Determine if can compute 2*x without overflow
* Examples: mul2OK(0x30000000) = 1
* mul2OK(0x40000000) = 0
*
* Legal ops: ~ & ^ | + << >>
* Max ops: 20
* Rating: 2
*/
int mul2OK(int x) {
return (((x>>31)&0x1)^((x>>30)&0x1))^0x1;
}
设计思想:计算2*x相当于逻辑左移一位,若x的符号位与第30位不同,则移位时符号位改变,溢出。
若x的符号位为1,则第30位为0时会溢出;若x符号位为0,则第30位为1时会溢出;于是将x的31位和30位分别与1相与,与1相与是本身,再异或,若相同异或为0,此时不溢出,由题意,再与1异或,使返回结果为1.
/*
* mult3div2 - multiplies by 3/2 rounding toward 0,
* Should exactly duplicate effect of C expression (x*3/2),
* including overflow behavior.
* Examples: mult3div2(11) = 16
* mult3div2(-9) = -13
* mult3div2(1073741824) = -536870912(overflow)
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 12
* Rating: 2
*/
int mult3div2(int x) {
int t = (x << 1) + x; //t = 3*x
int LSB = t & 0x1; //取出最低位
int sign = (t >> 31) & 0x1; //取出符号位
int ans = (t >> 1); //ans = 3*x/2;
ans += (LSB & sign); //rounding toward 0;
return ans;
}
设计思想:左移一位为x* 2,再+x即x* 3。
/*
* subOK - Determine if can compute x-y without overflow
* Example: subOK(0x80000000,0x80000000) = 1,
* subOK(0x80000000,0x70000000) = 0,
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 20
* Rating: 3
*/
int subOK(int x, int y) {
int z;
z=x+(~y+1);
z=(z>>31)&0x1;
x=(x>>31)&0x1;
y=(y>>31)&0x1;
return ((x^y)&(x^z))^0x1;
}
设计思想:x符号位与y符号位不同,且x符号位与(x-y)符号位也不同时,即溢出。
写法二:
int subOK(int x, int y) {
int ans = x + (~y + 1); //ans = x - y;
return !(((ans ^ x) & (x ^ y)) >> 31);
//当且仅当两个加数的符号位相同,而与他们的和的符号位不同,发生溢出
}
/*
* absVal - absolute value of x
* Example: absVal(-1) = 1.
* You may assume -TMax <= x <= TMax
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 10
* Rating: 4
*/
int absVal(int x) {
int y=x>>31;
x=(y&(~x+1))+((~y)&x);
return x;
}
设计思想:y = x>>31为x的符号位,当x符号位为0时,x绝对值就是x;当x符号位为1时,x绝对值是(~x+1)。
/*
* float_abs - Return bit-level equivalent of absolute value of 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 representations of
* single-precision floating point values.
* When argument is NaN, return argument..
* Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while
* Max ops: 10
* Rating: 2
*/
unsigned float_abs(unsigned uf) {
int x=uf&0x7fffffff;
if(x>0x7f800000)
return uf;//NaN
else
return x;
}
设计思想:
NaN(Not a Number,非数)表示未定义或不可表示的值。常在浮点数运算中使用。NaN 属性代表一个“不是数字”的值。这个特殊的值是因为运算不能执行而导致的,不能执行的原因要么是因为其中的运算对象之一非数字(例如, “abc” / 4),要么是因为运算的结果非数字(例如,除数为零)。
NaN表示的数阶码全为1,且小数域为非0。1111 1111 **** **** **** **** **** ***
解答过程与思考:
如果uf是NaN 返回NaN 否则,返回f而且可以用if语句
0 1111 1111 0000 0000 0000 0000 0000 000
只要后面32位中有一位不是0就是NaN
首先判断 是否是NaN
将uf的符号位变为0,得到的数若比0x7f80000大,即为NaN。
/*
* float_f2i - 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
* 返回unsigned uf的整型数的二进制形式
* 参数和结果都会被作为unsigned返回,但是会表示为二进制的单精度浮点值
* 任何超过范围的数都应该返回 0x80000000u.
* 允许的操作符: 任何整数或者无符号数操作符包括: ||, &&. also if, while
* 最多操作符数目: 30
* 分值: 4
*/
int float_f2i(unsigned uf) {
int x,y;
unsigned mini=0x80000000;
x=(uf>>23)&0xff;//阶码域
y=(uf&0x007fffff)^0x00800000;
if(x>158)
return mini;
if(x<127)
return 0;
else if(((uf>>31)&0x1)==1)
{
if(x>150)
return ((~(y<<(x-150)))+1);
else
return ((~(y>>(150-x)))+1);
}
else
{
if(x>150)
return (y<<(x-150));
else
return (y>>(150-x));
}
}
设计思想:将uf左移23位并与0xff相与来取出阶码域;设置y来取出小数域,并令其第23位为1,为了规格化的值隐含的以1开头的表示。
当阶码的值E大于31时,表示float转化为int会溢出,或者出现阶码域全为1的情况;当阶码的值小于0,返回0;当uf最高位为1时,判断x与150的大小,决定移位的方向,并以补码形式将数值存储;当uf最高位为0时,判断x与150大小,决定移位的方向。