需要解决的问题:
1.位运算优势以及优先级
2.位运算和逻辑运算符的对比
3.位运算的应用(获取最后一位,快速取模,交换两数,幂运算等等)
位运算的优势:
位运算是一种简洁且效率高的算法。
网上资料如下:
程序中的所有数在计算机内存中都是以二进制的形式储存的。位运算就是直接对整数在内存中的二进制位进行操作。
位操作的优势
- 位运算是一种底层的运算,往往比我们普通的运算要快上许多许多
- 位运算是最高效而且占用内存最少的算法操作,执行效率非常高
- 位运算操作的是二进制数,会拥有一些二进制的特性,在实际问题可以方便运用
- 位运算只需较低的空间需求
- 位运算使用能使程序变得更加简洁和优美
位运算符的种类
(1)逻辑
- ^按位异或:对应二进制位上的数字相同为0,否则为1。
- | 按位或:对应二进制位上的数字有1则为1,否则为0。
- & 按位与:对应二进制位全为1则为1,否则为0。
- ~ 按位取反:对一个数的二进制位按位取反(包括符号位)。
(2)移动
- << 左移:将二进制数整体左移,左边挤掉,右边空位补0.相当于将原值乘以2。
- >> 右移:二进制数整体右移,右边的位被挤掉,对于左边移出的空位,如果是正数则空位补0,若为负数,可能补0或补1,这取决于所用的计算机系统。
位运算的优先级
- 位移运算符优先级为5。
- 按位取反优先级为2,其余逻辑位运算符在8到10。
按位逻辑和逻辑运算符的区别
位运算的操作对象只能是整型或字符型数据,而逻辑运算符的操作对象为表达式,但是它们的原则是相同的,即:1.与:全真才为真 2.或:有一个为真即可。
至于记忆,这点倒是不需要特意去记,因为"或"和"与"两个字本身就已经揭示了它们的运算逻辑。
位操作符的应用(注意:位运算符是不会改变变量的值的)
我在网上看到的应用口诀:
清零取反要用与,某位置一可用或。
若要取反和交换,轻轻松松用异或。
(1)判断某数二进制数的任意一位:
#include<stdio.h>
int main(){
/*使用按位与&*/
int a = 5, b = 1;//0101
//第一种判断,若要知道第i位的数是否为1,只需将a向右移i - 1个单位
printf("%d", b & (a>>1));
//第二种判断,若要知道第i位的数是否为1,则将b赋值为2^(i - 1)次方,即将b左移i - 1位
b <<= 2;
printf("%d", b & a);//只要结果不为0,则该位置数为1.
return 0;
}
(2)判断奇偶性,和(1)原理一致。if(a & 1 == 0)则为偶数。
(3)清零某位数,和(1)原理一致。a=a& ~(1< < k - 1)。
(4)判断一个数是否为2的指数。if(x & (x - 1) == 0)则为2的指数。
(5)交换两数:
#include<stdio.h>
int main(){
int a = 5, b = 7;// a -> 0101;b -> 0111;
a ^= b; //a -> 0010
b ^= a; //b -> 0101
a ^= b; //a -> 0111
return 0;
}
(6)求绝对值(abs函数)
原理摘自网络:
在计算机基础课程里面我们知道,对于负数而言,原码转补码是 除符号位取反加1,补码转原码也是 除符号位取反加1。同时,如果我们求出了一个负数的原码,那么它的绝对值就很好求了,只需将这个原码的符号位取反即可(将1取反为0)。梳理一下,补码转原码时,除符号位都取反了,原码转绝对值时,符号位被取反了,那么,其实我们可以将这两次取反操作合在一起,即:对补码的所有位取反,然后加1,可得到该补码对应的负数的绝对值。
假设a是32位负整数,那么
a的符号位是1,(a >> 31) 是 111...111,一共32个1,也就是-1的补码,
和1进行异或 相当于 取反,即 0 ^ 1 = 1,1 ^ 1 = 0,
x + 1 相当于 x - (-1),x是任意数,
那么将a按位取反的算法是:a ^ (a >> 31),
将a将位取反加1的算法是:(a ^ (a >> 31)) + 1 == (a ^ (a >> 31)) - (-1) == (a ^ (a >> 31)) - (a >> 31)。其实这个算法同样适用于正整数求绝对值,因为,
假设b是32位正整数,那么
b的符号位是0,(b >> 31) 是 000...000,一共32个0,也就是0的补码,
和0进行异或 相当于 不变,即 0 ^ 0 = 0,1 ^ 0 = 1,
那么(b ^ (b >> 31)) - (b >> 31) == b。
————————————————
版权声明:本文为CSDN博主「choumin」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/choumin/article/details/103184120
#include<stdio.h>
int main(){
int y , x = -5;
y = x >> 31 ;
printf("%d", s = (x ^ y) - y) ; //or: (x + y) ^ y;
return 0;
}
(7) 使特定位的值取反 ,mask中特定位置1,其它位为0,与要被取反的数按位异或。
(8)取模运算:
原理为:a % (2 ^ n) <=> a & (2 ^ n - 1)。
(9) >> and <<可实现2的指数的运算
以下摘录我的实验报告中的一道题以及自己写的代码:
#include<stdio.h>
unsigned mod(unsigned a, unsigned b, unsigned c){
unsigned int x1,sum = 0,i;
for(i = 0;b > 0;i++){
sum += (a * ((b & 1) << i)) % c;
b >>= 1;
}
return sum;
}
int main(){
unsigned int a, b, c;
printf("Input unsigned integer numbers a, b, c:\n");
scanf("%u%u%u", &a, &b, &c);
printf("%u*%u%%%u=%u\n", a, b, c, mod(a,b,c));
return 0;
}
这是我第一次采用位运算来代替手写的幂函数,很香。最让我觉得妙的地方就在于,数组的下标恰好对应了指数。
(9)x 的 相反数 表示为 (~x+1)。
原理解释:互为相反数的两个数的原码形式只在符号位不同,正数的原反补相同,因此直接改变符号位即可;负数需要先将补码转化为原码再改变符号位,即如公式所描述的按位取反再加一。
小结:
这是我的第一篇博客,昨天做完了第一次实验报告感觉学到了挺多东西,因此决定记录下来。而今天为了尽可能保证这篇博客的完整性,我当然又学到了新东西。这只是实验报告的部分内容,后面我会抽时间记录整数,小数和IEEE单精度浮点数转化的方法。鉴于本人的水平有限,这篇博客的内容以及我的代码必然有许多可以补充和优化的地方。如果有的话,后面我大概会补充更多的个人理解和优化。