位运算及其应用

发布于:2022-11-07 ⋅ 阅读:(665) ⋅ 点赞:(0)

需要解决的问题:

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单精度浮点数转化的方法。鉴于本人的水平有限,这篇博客的内容以及我的代码必然有许多可以补充和优化的地方。如果有的话,后面我大概会补充更多的个人理解和优化。

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到