目录
- (取负)
- (两数相或)
- (选择运算符)
- (对某个字节取反)
- 14.isPower2
- 15.sm2tc
(取负)
要求:将所给数x取负。
操作:将操作数取反+1。
/*
* negate - return -x
* Example: negate(1) = -1.
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 5
* Rating: 2
*/
int negate(int x) {
return ~x+1;
}
(两数相或)
要求:只利用 ~ 和 & 操作,将数 x 和 y 相或。
操作:
取或,等价于将两个数的取反值~x, ~y相与后,再取反。
/*
* bitOr - x|y using only ~ and &
* Example: bitOr(6, 5) = 7
* Legal ops: ~ &
* Max ops: 8
* Rating: 1
*/
int bitOr(int x, int y) {
return ~(~x&~y);
}
(选择运算符)
要求:用逻辑运算实现三目运算:x ? y : z
。
思路:
构造一个数 f
,方便选择输出。f = ~(!x) + 1;
当x为0时,f 的每一位全为1;当x不为0时,f 的每一位全为0。
- 取1的反(
11111..110
) +1 后,所有位都为1; - 取0的反(
11111..111
) +1 后,所有位都为0(溢出)。
之后选择输出y或z:return (z & f) + (y & ~f);
int conditional(int x, int y, int z) {
int f = ~(!x)+1; //x为0,f每位全为1;x不为0,f每位全为0。
return (y & ~f) + (z & f);
}
(对某个字节取反)
要求:将所给数 x 的第 n 个字节取反。
思路:
取反可以用数位为1的数进行异或(^)操作,因为:
- 原来数位为1,和1取异或后变为0;
- 原来数位为0,和1取异或后变为1。
所以可以构造只有当前字节所在数位为1,其他位为0的数,和所给数相异或。
一个字节占 8 个数位。
第 n 个字节,需要将数 0xff
(11111111 = 255) 左移 8*n 个数位。
但是不能用乘法,所以可以将 8 表示为 2^3,构造数 y 为:y = n << 3
。
之后和 x 相异或,就把第n个字节取反了。
/*
* 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 t = 0xff; //255:11111111
t <<= n<<3; //左移n*8位,变为00000..0011111111000..00
return x ^ t;
}
经验: 取反操作,异或上1。
要求:判断数x的偶数数位是否都为1。
思路:
方法1:
int类型的数,偶数都为1,便是:01010101…0101。
所以先将数x的奇数数位全部变为0,然后判断其是否和上面的数相等就行了。
如何将数x的奇数数位全部变为0,偶数数位不变?
构造奇数数位全为0,偶数数位全为1的数y,和数x相与。
如何判断数x是否为 01010101…0101?
构造出该数,和数x相异或,看结果是否为0。若为0,则完全一致。
如何构造出奇数数位全为0,偶数数位全为1的数(01010101…0101)?
先构造出8位,再进行左移操作后相或。
int y=0x55; y |= y<<8; y |= y<<16;
方法2:
构造奇数数位全为1,偶数数位全为0的数(10101010…1010),判断其和数 x 相或后是否所有数位全为1。如果是,则说明数 x 的偶数数位全为1。
如何判断是否一个数位全为1呢?
取反看其是否为0。或者直接判断其是否为11111(取^操作)
/*
* allEvenBits - return 1 if all even-numbered bits in word set to 1
* Examples allEvenBits(0xFFFFFFFE) = 0, allEvenBits(0x55555555) = 1
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 12
* Rating: 2
*/
int allEvenBits(int x) {
//方法1:
int y=0x55;
y |= y<<8;
y |= y<<16; //构造32位偶数位置全为1,奇数位置全为0的数:0101 0101...
x &= y; //先将x的奇数位上的值全部变为0
return !(x^y); //如果x^y=0的话,说明两个数完全一致,返回1;否则返回取!结果:0。
// return x==y;
//方法2:
int y=0xAA;//,f=0xff;
y = y|y<<8;
y = y|y<<16;
// f |= f<<8;
// f |= f<<16;
return !~(x|y);
// return !((x|y)^f);
要求:将数 x 的第 n 个字节和第 m 个字节互换。
思路:
方法1:
将第 n 个字节化为 0,再和 “第 m 个字节” 相或。构造的 “第 m 个字节” 为:第 m 位不变,其他位为 0。
再将第 m 个字节化为 0,再和 “第 n 个字节” 相或。
方法2:
将第 n 个字节化为 1,再和 “第 m 个字节” 相与。 构造的 “第 m 个字节” 为:第 m 位不变,其他位为1。这个不好构造,需要 第m位 和 第m位为0,其他位为1的数 相或。
再将第 n 个字节化为1,再和 “第 n 个字节” 相与。
/*
* byteSwap - swaps the nth byte and the mth byte
* Examples: byteSwap(0x12345678, 1, 3) = 0x56341278
* byteSwap(0xDEADBEEF, 0, 2) = 0xDEEFBEAD
* You may assume that 0 <= n <= 3, 0 <= m <= 3
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 25
* Rating: 2
*/
int byteSwap(int x, int n, int m) {
int a = x>>(n<<3) & 0xff; //将第n个字节提出来
int b = x>>(m<<3) & 0xff;
//方法1:
x &= ~(0xff<<(n<<3)); //第n个字节化为0
x |= b<<(n<<3); //和第m位相或
x &= ~(0xff<<(m<<3));
x |= a<<(m<<3);
//方法2:
x |= 0xff<<(n<<3); //将第n个字节化为1
x &= (~(0xff<<(n<<3)) | (b<<(n<<3))); //和第m位相与,注意这个需要其他数位为1
x |= 0xff<<(m<<3);
x &= (~(0xff<<(m<<3)) | (a<<(m<<3)));
return x;
}
但是一开始想的做法不知道为什么不对:
将第n个字节上的值全部变为1,再和第m个字节上的值相与。
将第m个字节上的值全部变为1,再和第n个字节上的值相与。
一周后更——
注意:提出来的第n个字节经过移位到想要的位置,其他位置变为0了。所以在和其他数相与的时候需要特别注意,将其他位置变为1。有点麻烦。
所以尽量用这个移过位的数和其他数相或。
要求:输出第 low 位到第 high 位为1,其余位置为0的数。若 low>high,返回0。
思路:
构造数位全为1的数x。
- 左移 low 位,使得第low位右边的全部数位变为0,记为 a。
- 左移high+1位,使得第high位及其后面的全部数位变为0,记为 b。
那么,数 a 和数 b 仅仅 [low,high]
位不同,其余位相同。
可以将两个数异或,便得出 [low,high]
为1,其余为0的数了。
如何满足当 low>high 的时候,变为 0? 将最终答案再与上 a 即可。
/*
* bitMask - Generate a mask consisting of all 1's
* lowbit and highbit
* Examples: bitMask(5,3) = 0x38
* Assume 0 <= lowbit <= 31, and 0 <= highbit <= 31
* If lowbit > highbit, then mask should be all 0's
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 16
* Rating: 3
*/
int bitMask(int highbit, int lowbit) {
int x=~0;
int a = x<<lowbit;
int b = x<<highbit<<1;
return (a ^ b) & a;
}
要求:给出 x,y,判断是否
x
≤
y
x≤y
x≤y ?
思路:
- x 和 y 的符号位不同:如果 x 的符号位为 1 则满足;
- x 和 y 的符号位相同:如果 y-x 的符号位为 0 则满足。
数 x 的符号位:最高位(第32位)。符号位为0为正数,为1为负数。
/*
* isLessOrEqual - if x <= y then return 1, else return 0
* Example: isLessOrEqual(4,5) = 1.
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 24
* Rating: 3
*/
int isLessOrEqual(int x, int y) {
int a=(x>>31),b=(y>>31); //x和y的符号位
int c=a+b; //a+b的符号位
int d=(y+(~x)+1)>>31; //y-x的符号位
return ((c&1)&a) | ((~c)&(!d));
//如果x和y的符号位不同,c为1,第0位为1;否则c为0或2,第0位为0。
//c为1时a也要为1,或者 c不为1且d为0。
}
但是有个疑问了,为什么定义 d=(x+(~y)-1)>>31
时,判断 (~c)&d
不行呢?两周后更 :
哦对,如果用y-x的话,如果y和x相等的话,符号位一样为0,和x<y的符号位一致;但是用x-y的话,x和y相等时,符号位为0,和x<y时的符号位(1)不一致,还需要特判。所以用y-x更简洁些。
要求:判断一个32位二进制数 x 能否由16位二进制数表示。
思路:
一个32位二进制数最后一位(第32位,从1开始数)为符号位,其余31位为有效位;一个16位二进制数最后一位(第16位)为符号位,其余15位为有效位。
如果一个32位二进制数可以由16位二进制数来表示的话,说明其符号位后面的所有位都是和符号位相同的。
也就说:如果这个数是正数的话,那么第16位到第32位需要都是0;如果是负数的话,都需要是1。
所以只需要判断这个数的最后17位是否全为0或者全为1便可。
/*
* fitsShort - return 1 if x can be represented as a
* 16-bit, two's complement integer.
* Examples: fitsShort(33000) = 0, fitsShort(-32768) = 1
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 8
* Rating: 1
*/
int fitsShort(int x) {
x >>= 15; //右移15位,剩下符号位(变为16位时)和最后的16位。
return !~x | !x; //判断剩下的17位是否符号相同,都为1或都为0.
}
如何判断是否所有位数都相同,都为0或都为1?
所有数位都为0好判断,即判断!x
是否为1。
那么判断一个数都为1,只需要将这个数取反~x
,判断取反之后是否为0便可。
要求:判断一个32位二进制数 x 能否由 n 位二进制数表示出来。
思路:
和上一题思路相同,只需要判断剩下的 32-n 位是否和符号位相同,即判断第 n 位(从1开始数)以及后面的所有位数是否都相同。
/*
* fitsBits - return 1 if x can be represented as an
* n-bit, two's complement integer.
* 1 <= n <= 32
* Examples: fitsBits(5,3) = 0, fitsBits(-4,3) = 1
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 15
* Rating: 2
*/
int fitsBits(int x, int n) {
x >>= (n + (~1+1)); //右移n-1位,除去有效位,判断符号位和其余位数是否都相同。
return !x | !~x;
}
要求:判断一个数 x 是否是 int 型最小值 tmin
。
思路:tmin
有性质:tmin + tmin = 0
. 所以 tmin = ~tmin+1.
但是要特判去掉同样满足的 0 [0x0]。
判断相等用异或,异或值为0则相等。
/*
* isTmin - returns 1 if x is the minimum, two's complement number,
* and 0 otherwise
* Legal ops: ! ~ & ^ | +
* Max ops: 10
* Rating: 1
*/
int isTmin(int x) {
return !( (x^(~x+1)) | (!x) );
}
判断 x 是否为 int 型最大值 tmax
?
tmax 有性质:tmax = ~(tmax+1)
。
但是要特判去掉同样满足的 -1[0xfffffff]。
int isTmax(int x){
return !( (x^(~(x+1)) | (!(~x)) )
}
经验:
- int 型最大值 tmax,最小值 tmin 性质:
t m i n = t m i n + 1 ; tmin = ~tmin+1; tmin= tmin+1;
t m a x = ( t m a x + 1 ) . tmax = ~(tmax+1). tmax= (tmax+1). - 判断相等用异或。
要求:判断两个数 int 型数 x,y 相加是否溢出。溢出返回0,否则返回1。
思路:
- 如果两个数符号不同,那么相加一定不会溢出。(因为两个数都是 int 范围内的,相加只会让一方抵消,绝对值不会增长)
- 否则,如果两数之和的符号和这两个数的符号相同,就说明没有溢出。(溢出:正数+正数=负数,负数+负数=正数)
所以判断 x, y
和 x+y
的符号便可。
/*
* addOK - Determine if can compute x+y without overflow
* Example: addOK(0x80000000,0x80000000) = 0,
* addOK(0x80000000,0x70000000) = 1,
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 20
* Rating: 3
*/
int addOK(int x, int y) {
/*
判断两个数相加是否溢出。
如果两数符号不同,相加不会溢出;
否则,如果和的符号位和两数的符号相同,则不会溢出。
*/
int a = x>>31; //x的符号位
int b = y>>31; //y的符号位
int c = (x+y)>>31; //x+y的符号位
return ( (a^b)|!(c^a) )&1; //判断相等用异或
}
但是有个问题,为什么最后要加个 &1
?
给出的错误数据为:x=0x80000000, y=0x7ffffffff,返回的是-1。
但是在本地测试确实返回的1。
咋回事?
要求:将 x 循环左移 n 位。循环左移:最高位移到最低位。
思路:
- 首先构造最后 n 位为1,其余位为 0 的掩码 mask。
- 将其与 x 右移 32-n 所得的数 相与,于是得到 x 最左端的 n 位,并放置到了最右端。
- x 左移 n 位,和 mask 相或,将最后 n 位补全。
就是,将最后 n 位取出,再放到前 n 位上。
如何构造最后 n 位为1,其余位为 0 的掩码 mask?
起初是想将全1右移32-n位,于是剩下最后n位在最右端,其余位为0。但是交上去不对。
错误之处:
全1表示为负数,而负数 右移 补位的是1,不是0;
但负数 左移 补位的却是0。
所以可以将全1左移n位,使得最后n位变为0。于是:
- 可以直接将该数取反,变为最后n位为1,其余位为0的数;
- 也可以仿照bitmask的做法:将该数与全1相异或,因为只有最后n位不同,所以最后n位变为1,其余位变为0;
- 也可以将全1加上(1<<n),通过相加溢出,将第n位左边的所有位数变为0。
/*
* 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 mask = (~0)>>(32+(~n)+1); 错误,负数右移补1,不是0。
int a = ~0;
int b = a<<n; //全1左移n位,使得最后n位为0。
// int mask = a^b; //a和b最后n位不同,异或,最后n位变为1,其余为0。
int mask = ~b; //取反
// int mask = (~0)+(1<<n); //巧用溢出
mask &= (x>>(32+(~n)+1));
x <<= n;
x |= mask;
return x;
}
经验:
左移操作:丢弃高位,0补低位。
右移操作:正数右移高位补0,负数右移高位补1;无符号负数高位补0
易错点:
负数右移补1,左移补0。
14.isPower2
要求:判断一个数 x 是否是 2 的次幂数。
思路:
如果是 2 的次幂数,那么 x 的二进制数只有一位是1,其余都是0。
那么 x-1 便是原来1位后面的所有位都是1。那么 x 和 x-1 相与便为0。
注意排除特殊情况:负数,0.
/*
* isPower2 - returns 1 if x is a power of 2, and 0 otherwise
* Examples: isPower2(5) = 0, isPower2(8) = 1, isPower2(0) = 0
* Note that no negative number is a power of 2.
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 20
* Rating: 4
*/
int isPower2(int x) {
int y = x+(~1)+1;
return !(x&y) & !(x>>31) & !!x;
}
15.sm2tc
要求:原码到补码的转换。给出原码表示x,返回其对应的补码表示。
思路:
右移31位,将结果存为 t
。
- 如果原数为正数,那么 t 此时全为0。而正数的补码不用改变;
- 否则,t 全为1(负数右移补1)。负数补码,取反+1,符号位变为1。
所以可以将 x 或上 t,正数的话,不改变;负数就会取反。
是负数的话,最后要加上100…001,即(1<<31)+1。
int sm2tc(int x) {
//原码到补码的转换
int t = x>>31; //t作为符号位
return (x^t) + (((1<<31)+1)&t);
// int flag;
// if(!t) flag=0;
// else flag = ((1<<31)+1); //最低位最高位都变为1.
// return (x^t) + flag;
}
剩下的两个浮点数运算就不更了,太难了。。