文章目录
一. 概念定义
1.1 右移运算符的定义
右移运算符是一个二元的位运算符,表示为 x >> y,其中x和y均为整数。
x >> y念作:将x向右位移y位。表示的意思是将x的二进制向右位移y位,对于正数头部用0补上,对于负数,头部用1补上。
例如:
(1010)₂ >> 2
=>(0010)₂
结果就是10>>2 = 2
1.2 右移的执行结果
右移的执行结果等价于(x >> y)
代码如下:
#include <stdio.h>
int main() {
int x = 0b1010;
int y = 2;
printf("%d\n", x >> y);
return 0;
}
结果为:2
1.3 负数右移的执行结果
所谓负数右移,就是在 x >> y 中,x为负数的情况。
代码如下:
#include <stdio.h>
int main() {
printf("%d\n", -1 >> 1);
return 0;
}
输出结果为:-1
它也符合「x / 2 ^ y」的执行结果(注意负数的向下取整与正数相反),这个可以用补码来解释:
-1的补码
1111 1111 1111 1111 1111 1111 1111 1111
右移一位后,首位补1:
1111 1111 1111 1111 1111 1111 1111 1111
刚好与-1的补码相等。所以我们可以认为-(x >> y) 与 -x >> y是等价的
例题1:要求不运行代码,分析出这段代码的输出结果为什么
#include <stdio.h>
int main() {
int x = (1 << 31) | (1 << 30) | 1;
int y = (1 << 31) | (1 << 30) | (1 << 29);
printf("%d\n", (x >> 1) / y);
return 0;
}
1.4 右移负数位是什么情况
刚刚我们讨论了x < 0的情况,那么现在我们考虑一下 y < 0的情况是如何的?
是否还满足:「x / 2 ^ y」
看如下例子:
#include <stdio.h>
int main() {
printf("%d\n", 1 >> -1); // 2
printf("%d\n", 1 >> -2); // 4
printf("%d\n", 1 >> -3); // 8
printf("%d\n", 1 >> -4); // 16
printf("%d\n", 1 >> -5); // 32
printf("%d\n", 1 >> -6); // 64
printf("%d\n", 1 >> -7); // 128
return 0;
}
这段代码在Dev上是可以运行的,但是会出现如下警告
编译器告诉我们尽量不要用右移负数,右移负数和左移对应的整数的效果是一样得
1.5 右移运算符的应用
1.5.1 去掉低k位
例题2:给定一个数x,去掉他的低k位后输出。
我们直接输出x >> k即可。
1.5.2 取低位连续一
例题3:获取一个数x低位连续的1并且输出。
对于一个数x,假设它的低位由连续k个1,
则对其进行加1
得:
而这两个数的异或结果为:
这时候右移一位则得到连续k个1的值,计算语句:(x ^ (x + 1)) >> 1;
1.5.3 取低k的值
例题4:取一个数x的低k位的值(0 <= k <= 30)
我们先将x向右移动k位,将低k位移动到最尾部,然后再与1进行按位与计算即可
计算公式:(x >> y) & 1
二. 推荐专栏
三. 相关练习
3.1 位1的个数
思路分析:
这道题我们需要应用一个公式n & (n - 1),其计算结果就是,将最低位的1去除,所以我们每去除以此,就++一次,直到n为0。
代码如下:
int hammingWeight(uint32_t n) {
int count = 0;
while(n){
n = n & (n - 1);//消去当前最低为的1
count ++;
}
return count;
}
3.2 2的幂
判断是不是2的幂我们只需对其进行按位与计算, n & (n - 1), 如果结果位0, 则为2的幂,否则不是
代码如下:
bool isPowerOfTwo(int n){
if(n <=0 ) return false;
return !(n &(n-1));
}
3.3 4的幂
方法一:
还是用试除法
bool isPowerOfFour(int n){
if(n == 1){
return true;
}
long long int t = 1;
while(t != n){
t *= 4;
if(t > n){
return false;
}
}
return true;
}
方法二:
我么可以发现4的所有幂的二级制形式中偶数位为1,奇数位为0,所以我们可以判断其奇数位是存在1,如果存在,则不是
代码如下:
bool isPowerOfFour(int n){
return n > 0 && (n & (n - 1)) == 0 && (n & 0xaaaaaaaa) == 0;
}
时间复杂度为O(1)
3.4 数字转换为十六进制数
位移法:
我们通过位移法对其进行计算,我们已知,一个十六进制位对应四个二进制位,所以我们对二进制进行4个4个的转化,我们通过(num>>i) & 0xf的方式将每四位二进制数分别取出,并存放在val中,然后再判断val是否小于10,如果小于10,则将对应的09计入在数组中,否则将af计入。最后我们还要将数组反转,因为我们是从尾部开始的。
代码如下:
void resever(char* s, int len){
int i = 0;
int j = len;
while(i < j){
char tmp = s[i];
s[i] = s[j];
s[j] = tmp;
i++;j--;
}
}
char * toHex(int num){
if(num == 0){
return "0";
}
char* ans = (char*)malloc(sizeof(char) * 10);
int ansSize = 0;
for(int i = 0; i < 32; i += 4){
int val = (num >> i) & 0xf;
char c = val < 10 ? val + '0' : (val - 10)+'a';
//printf("%d ", val & 0xf < 10);
if(val || (num >> i)){//判断num是否为0
ans[ansSize++] = c;
}
}
ans[ansSize] = '\0';
resever(ans, ansSize - 1);
return ans;
}