按位与运算符(&)
参加运算的两个数据,按二进制位进行“与”运算
运算规则:两位同时为1结果才为1,否则为0.
负数按补码形式参加按位与运算
“与运算”的特殊用途:
(1)清零。如果想将一个单元清零,即使其全部二进制位为0,只要与一个各位都为0的数值相与,结果为0.
(2)取一个数中指定位
方法:找一个数,对应X要取的位,该数的对应位为1,其余位为0,此数与X进行“与运算”可以得到x中的指定位。
按位或运算符(|)
特殊作用:
(1)常用来对一个数据的某些位置1.
方法:找到一个数,对应X要置1的位,该数的对应位为1,其余位为0,。此数与x相或可使x中的某些位置1.
例:将x=10100000的低4位置1,用x|0000 1111=1010 1111可得到。
异或运算符(^)
参加运算的两个数据,按二进制位进行“异或”运算
运算规则:参加运算的两个对象,如果两个相应位为“异”(值不同),则该位结果为1,否则为0。
特殊作用:
(1)使特定位翻转找一个数,对应X要翻转的各位,该数的对应位为1,其余位为0,此数与X对应位异或即可。
例:x=10101110,使x低4位翻转,用x^0000 1111=1010 0001即可得到。
(2)与0相异或,保留原值,X^00000000=1010 1110。
按位异或其实就是不进位加法,如1+1=0,0+0,1+0=1.
异或的几条性质:
1. 交换律
2. 结合律( a ^ b ) ^ c == a ^ ( b ^ c )
3. 对于任何数x,都有x^x=0,x^0=x
4. 自反性:a ^ b ^ b = a ^ 0 = a;
异或运算最常见于多项式除法,不过它最重要的性质还是自反性:A XOR B XOR B = A,即对给定的数A,用同样的运算因子(B)作两次异或运算后仍得到A本身。这是一个神奇的性质,利用这个性质,可以获得许多有趣的应用。 例如,所有的程序教科书都会向初学者指出,要交换两个变量的值,必须要引入一个中间变量。但如果使用异或,就可以节约一个变量的存储空间: 设有A,B两个变量,存储的值分别为a,b,则以下三行表达式将互换他们的值 表达式 (值) :
a=a^b;
b=b^a;
a=a^b;
应用举例:
1~1000放在1001个元素的数组中,只有唯一的一个元素值重复,其他均只出现一次。每个数组元素只能访问一次,设计一个算法,将它找出来;不用辅助存储空间,能否设计一个算法实现?
解法一:把所有数加起来,减去1+2+……+1000的和。但问题是如果数列过大,则可能会导致溢出。
解法二:将所有数全部异或,得到的结果与1^2^3^…^1000的结果进行异或,得到的结果就是重复数。
左移运算符(<<)
将一个运算对象的各二进制位全部左移若干位(左边的二进制位丢弃,右边补0)。
例:a=a<<2将a的二进制位左移2位,右补0,左移1位后a=a*2;
右移运算符(>>)
将一个数的各二进制全部右移若干位,正数左补0,负数左补1,右边丢弃。
操作数每右移一位,相当于该数除以2.
例如:a=a>>2将a的二进制位右移2位,左补0 or 补1得看被移数是正还是负。
符合赋值运算符:
&= 例:a &=b 相当于a=a& b
|= 例:a |=b 相当于a=a |b
>>= 例:a >>=b 相当于a=a>> b
<<= 例:a<<=b 相当于a=a<< b
^= 例:a ^= b 相当于a=a^ b
不同长度的数据进行位运算
如果两个长度不同的数据进行位运算时,系统会将二者按右端对齐,然后进行位运算。
以“与”运算为例:
如果一个long类型数据与一个int类型数据进行“与”运算,右端对齐后,左边不足的位依下面三种情况补足,
(1)如果整型数据为正数,左边补16个0。
(2)如果整型数据为负数,左边补16个1。
(3)如果整型数据为无符号位,左边也补16个0。
位运算:将一个字符型二进制数字转换成10进制
int change(char *st)
{
int num = 0;
while (*st)
num = (num << 1) + (*st++ - '0');
return num;
}
int main()
{
char *pbin = "01001001";
change(pbin);
printf("%d", change(pbin));
}
位操作的运用
1. 奇偶性的判断
对于二进制表示来说,其最右位为1则该数为奇数,最右位为0则该数为偶数。
int a;
cin>>a;
if( a & 1)
cout<<”奇数”<<endl;
if( ( a & 1 ) ==0)
cout<<”偶数”<<endl;
2. 不借助变量交换两个数
a=a^b;
b=a^b;
a=a^b;
3. 取相反数
将正数n变为-n,将负数n变为-n。
将整数取反加1即可得到相反数:
常见等式:- n = ~ ( n - 1 ) = ~ n + 1
例子:求某整数的绝对值
int my_abs(int num)
{
if ( num < 0 )
{
return ~num + 1;//取反加一的到相反数
}
return num;
}
4. 整数二进制表示中最右边的1
获取整数二进制表示中最右侧的1:n & (-n)等价于n & ~(n-1)
例如,n=1100,-n=0100,那么n & ( -n )=0100,这样就获取到了二进制表示中最右侧的1。
去除整数二进制表示中最右侧的1:n & (n-1)
例如,n=1100,n-1=1011,那么n & (n-1)=1000,这样就去除了二进制表示中最右侧的1。
5.32位系统某数右移/左移32位或更多位
在移位运算时,byte、short和char类型移位后的结果会变成int类型,对于byte、short、char和int进行移位时,规定实际移动的次数和32的余数,也就是移位33次和移位1次得到的结果相同。
位运算相关题目
1、二进制中1的个数
用到了n & (n - 1)
(1)方法一:参考文章:整数的二进制表示中1的个数
方法二:二进制位的翻转和二进制表示中1的个数
(2) 输入两个数A和B,输出将A转换为B所需改变的二进制的位数。
方法:首先,A异或B得到的是A和B中不相同位数组成的数,然后再求这个数二进制表示中1的个数,即为所求。
2、位操作的加减运算(用位操作实现加减)
加法和减法相似,因为根据异或的自反性,a+b也就相当于a+(-b); 相反数就是取反加一。
//1.加法操作
int binaryadd(int a, int b)
{
int ca;
int add;
do
{
add = a^b;//得到本位的加法结果
ca = (a&b) << 1;//得到该位对高位的进位值
a = add;
b = ca;
} while (ca != 0);//循环直到某次运算没有进位,运算结束
return add;
}
//2.减法操作
int binarysub(int a, int b)//减法操作就是加上一个负数
{
return binaryadd(a, binaryadd(~b, 1));
}
3、求两个数中较小的那个
y ^ ((x ^ y) & -(x < y))
分析:当x < y时,-(x < y)为-1,其补码形式全为1,则(x ^ y) & -(x < y) = x ^ y,则上述表达式返回的是较小的数x;
当x >= y时,-(x < y)为0,补码形式为全0,则(x ^ y) & -(x < y) = 0, 则表达式返回的是较小的数y