位运算的几种用法
无论在C语言的学习的过程中还是ARM裸机的学习过程中,我们总会遇到有关位运算的问题,这里就细数一下那些位运算的骚操作吧!!!。
1.置位和位清除
开发过程中时常会操作寄存器,比如一个16位的,每一位都有不同的作用,我们要在不影响其他位的情况下,对特定的某一位(某些位)来就行置位、位清除操作。
置位操作结合或运算|、位清除操作结合与运算&和~取反
比如ARM裸机开发中要点亮LED,首先要对GPFCON寄存器进行配置,需要将此寄存器中的[9:8]位配置为01
相当于对位9清除、位8置位
GPFCON |= (1<<8);
GPFCON &= ~(1<<9);
2.交换变量
初学C语言时,交换变量的操作无非有俩种:引入新变量、相加相减
引入新变量很简单,这里简单提及一下俩变量相加相减的方法:
int a=2,b=3;
a = a+b; //a=2+3
b = a-b; //b=2+3-3=2
a = a-b; //a=2+3-2=3
还可以通过位运算进行俩个变量的交换,就是异或运算^,对异或运算要了解:
俩个相同的数异或结果为0,一个数异或0的结果是这个数本身。
所以,利用异或运算可以交换俩个变量:
int a=2,b=3; //a=010 b=011
a = a^b; //a=010^011
b = a^b; //b=010^011^011=010^0=010
a = a^b; //a=010^011^010=011^0=011
/*用一句代码也可以实现*/
a^=b^=a^=b;
利用异或的性质:相同的数异或为0,与0异或为本身。
3.判断整数奇偶性
判断一个二进制数的奇偶性,只需要看0位的值是1还是0.
if( x&1 == 1 )
printf("奇数\n");
else
printf("偶数\n");
4.求2的次方
移位:左乘右除
x << n ; //x乘以2的n次方
x >> n ; //x除以2的n次方
x << 3 ; //x*2^3
x >> 4 ; //x/(2^4)
5.求俩个整数的平均数
求俩个数的平均数就是和除以2,可以用移位运算来实现:
x = (a+b) >> 1; //x为a、b的平均数
6.取int绝对值
/*取int型数据x的绝对值*/
int abs( int x )
{
return ( x^(x>>31) - (x>>31) );
}
如果x为正数,即最高位符号位是0,则(x>>31)=0,x^0=x,返回的是x;
如果x为复数,即最高位符号位是1,则(x>>31)=-1,x^(-1),要计算补码再进行异或运算,结果为|n|-1(绝对值n-1),所以在后面要通过:-(x>>31)来+1.使结果为-n,即n的绝对值。
7.比较俩个int数的大小
以前比较俩个数的大小都是采用三目运算符:a>b?a:b
,可以采用位运算来求出俩个数的大小,如示,调用max函数既可以返回较大的一个数:
方法一:
和-1作与运算结果不变
/*输出最大数*/
int max( int a, int b )
{
return b & ( (a-b)>>31 ) | a & ( ~(a-b)>>31 );
}
如果b大,则(a-b)>>31为-1,b&-1=b,或运算|第一个为真,结束运算,直接返回b;
如果a大,则(a-b)>>31为0,b&0=0,或运算|进入下一个判断,(~(a-b)>>31)=-1,a&-1=1,返回a。
方法二:
与0异或为本身,与自己异或为0
/*输出最大数*/
int max( int a, int b )
{
return a ^ ( (a^b) & -(a<b) );
}
首先要判断(a<b)的真假:
如果(a<b)为真,即1,即(ab)&-1=ab,则表达式就变为aab=b,返回较大的b;
如果(a<b)为假,即0,即(ab)&0=0,则表达式就变为a0=a,返回较大的a。
8.判断俩个int数符号是否相同
利用了最高位符号位:正数符号位为0,负数符号位为1
(a^b) < 0 //a、b异号
(a^b) >= 0 //a、b同号
9.不用加减乘除作加法
题目: 写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。
先了解十进制中的加法步骤,比如:21+192
首先,所有位相加,不进位,得到113
然后,计算进位值,得到100
然后,进位值与相加值(不进位)相加,得到213
然后,计算进位值,得到0
最后,当新的进位值为0时,即得到最终结果
这就是十进制下的加法,可以看出来,十进制加法的求解时,是处于一个循环的,先在每个位做不进位加法,然后求出进位值,然后使俩者相加,循环,直到进位值为0,得出最终结果。
所以,核心思想在于:
- 每个位实现不进位加法
- 求出进位值
- 循环上述直到进位值为0
在二进制中,俩个数的不进位加法就是异或运算^,求进位值就是与运算左移一位。
不进位加法:a^b
求进位值 :(a&b)<<1
所以,思路就是不断求不进位加法,直到进位值为0
所以初步的实现就如下:
int sum( int a, int b )
{
int temp1=0,temp2=1;
do
{
temp1 = a^b;
temp2 = (a&b)<<1;
a=temp1;
b=temp2;
}while(temp2!=0);
return temp1;
}
在do-while循环里面实现
首先,作第一次的不进位加法 temp1 = a^b;
然后,看看有没有进位值:temp2 = (a&b)<<1;
如果,有进位值:temp2!=0
则,将进位值与不进位的结果再进行不进位加法
然后,再看看有没有进位值
…
直到进位值为0,退出循环
结果就是temp1!!!