位与(&)、位或(|)、异或(^) 、左移<<、右移>>、原码、反码、补码的简单应用

本文详细介绍了位运算,包括位与(&)、位或(|)、异或(^)的操作规则及其在编程中的特殊应用。位运算在处理二进制数据时具有重要作用,如清零、取位、翻转等。同时,文章阐述了原码、反码、补码的概念,解释了它们在计算机存储数字时的编码方式,并通过实例展示了如何进行转换。此外,还探讨了位运算在解决特定问题,如寻找数组中重复元素和两个未出现数字的算法上的应用。
摘要由CSDN通过智能技术生成

一、位与(&)、位或(|)、异或(^)

1.位与运算(&):两位同时为1,结果才为1,否则为0

参加运算的两个数据,按二进制位进行“&”运算。

运算规则:0&0=0;  0&1=0;   1&0=0;    1&1=1;

例如:3&5  即 0000 0011& 0000 0101 = 00000001  因此,3&5的值得1。

“与&运算”的特殊用途(负数按补码形式参加按位与运算)

(1)清零:如果想将一个单元清零,即使其全部二进制位为0,只要与一个各位都为零的数值相与,结果为零。

(2)取一个数的指定位:找一个数,对应X要取的位,该数的对应位为1,其余位为零,此数与X进行“与运算”可以得到X中的指定位。

例:设X=10101110,  取X的低4位,用 X & 0000 1111 = 00001110 即可得到;

int main()
{
	//有符号的十六进制转化为十进制数
	int a = 0xFF9C;

	//清除符号位
	int b = a & 0x7FFF;
 
	//反码
	int c = ~b;
 
	//清除左边多余位
	int d = c & 0x7FFF;
 
	//加1
	d = d + 1;

	//符号位
	int e = d * -1; 
	printf("e=%d\n", e); //e = -100
}

2.位或运算(|):只要有一个为1,其值为1,否则为0

参加运算的两个对象,按二进制位进行“或”运算。

运算规则:0|0=0;  0|1=1;  1|0=1;   1|1=1;

例如:3|5 即 00000011 | 0000 0101 = 00000111  因此,3|5的值得7。

“或|运算”特殊作用(负数按补码形式参加按位或运算)

常用来对一个数据的某些位置1:找到一个数,对应X要置1的位,该数的对应位为1,其余位为零。此数与X相或可使X中的某些位置1。

例:将X=10100000的低4位置1 ,用X | 0000 1111 = 1010 1111即可得到。

3.异或运算(^):异值为1,否则为0

参加运算的两个数据,按二进制位进行“异或”运算。

运算规则:0^0=0;  0^1=1;  1^0=1;   1^1=0;

即:参加运算的两个对象,如果两个相应位为“异”(值不同),则该位结果为1,否则为0。

例如:3^5 即 0000 0011 | 0000 0101 = 00000110  因此,3^5的值为6。

异或运算”的特殊作用:

(1)使特定位翻转找一个数,对应X要翻转的各位,该数的对应位为1,其余位为零,此数与X对应位异或即可。

例:X=10101110,使X低4位翻转,用X ^0000 1111 = 1010 0001即可得到。

(2)与0相异或,保留原值 ,X ^ 00000000 = 1010 1110。

下面重点说一下按位异或,异或其实就是不进位加法,如1+1=0,,0+0=0,1+0=1。

例:(1)1-1000放在含有1001个元素的数组中,只有唯一的一个元素值重复,其它均只出现
一次。每个数组元素只能访问一次,设计一个算法,将它找出来;不用辅助存储空
间,能否设计一个算法实现?
解法一、显然已经有人提出了一个比较精彩的解法,将所有数加起来,减去1+2+...+1000的和。
这个算法已经足够完美了,相信出题者的标准答案也就是这个算法,唯一的问题是,如果数列过大,则可能会导致溢出。
解法二、异或就没有这个问题,并且性能更好。
将所有的数全部异或,得到的结果与1^2^3^...^1000的结果进行异或,得到的结果就是重复数。

(2)一系列数中,除两个数外其他数字都出现过两次,求这两个数字,并且按照从小到大的顺序输出.例如 2 2 1 1 3 4.最后输出的就是3 和4,见如下代码:

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
using namespace std;
#define N 1000010
int a[N];
 
int main()
{
    int t;
    scanf("%d", &t);
    while(t--) {
        int n;
        scanf("%d", &n);
        int x = 0;
        for(int i = 1; i <= n; i++) 
        {
            scanf("%d", &a[i]); x ^= a[i];
        }
        int num1 = 0, num2 = 0;
        int tmp = 1;
        while(!(tmp & x)) tmp <<= 1;
		cout<<tmp<<endl;
        for(int i = 1; i <= n; i++) 
        {
            if(tmp & a[i]) num1 ^= a[i];
            else num2 ^= a[i];
        }
        printf("%d %d\n", min(num1, num2), max(num1, num2));
    }
    return 0;
}
 


 

4、左移运算符(<<)

将一个运算对象的各二进制位全部左移若干位(左边的二进制位丢弃,右边补0)。

例:a = a<< 2将a的二进制位左移2位,右补0,

左移1位后a = a *2;

若左移时舍弃的高位不包含1,则每左移一位,相当于该数乘以2。


5、右移运算符(>>)

将一个数的各二进制位全部右移若干位,正数左补0,负数左补1,右边丢弃。

操作数每右移一位,相当于该数除以2。

例如:a = a>> 2 将a的二进制位右移2位,

左补0 or 补1得看被移数是正还是负。
 

二、学习原码、反码、补码前的补充

1.机器数和真值

在学习原码, 反码和补码之前, 需要先了解机器数和真值的概念.

2.机器数

一个数在计算机中的二进制表示形式,  叫做这个数的机器数。机器数是带符号的,在计算机用一个数的最高位存放符号, 正数为0, 负数为1.

比如,十进制中的数 +3 ,计算机字长为8位,转换成二进制就是00000011。如果是 -3 ,就是 10000011 。

那么,这里的 00000011 和 10000011 就是机器数。

2、真值

因为第一位是符号位,所以机器数的形式值就不等于真正的数值。例如上面的有符号数 10000011,其最高位1代表负,其真正数值是 -3 而不是形式值131(10000011转换成十进制等于131)。

所以,为区别起见,将带符号位的机器数对应的真正数值称为机器数的真值。例:0000 0001的真值 = +000 0001 = +1,1000 0001的真值 = –000 0001 = –1

三、 原码、反码,、补码的基础概念和计算方法.

在探求为何机器要使用补码之前, 让我们先了解原码, 反码和补码的概念.对于一个数, 计算机要使用一定的编码方式进行存储. 原码, 反码, 补码是机器存储一个具体数字的编码方式.

1. 原码

原码就是符号位加上真值的绝对值, 即用第一位表示符号, 其余位表示值. 比如如果是8位二进制:

[+1]原 = 0000 0001

[-1]原 = 1000 0001

第一位是符号位. 因为第一位是符号位, 所以8位二进制数的取值范围就是:

[1111 1111 , 0111 1111]==>[-127 , 127]

2. 反码

反码的表示方法是:

(2-1)正数的反码是其本身;

(2-2)负数的反码是在其原码的基础上, 符号位不变,其余各个位取反。

[+1] = [00000001]原 = [00000001]反

[-1] = [10000001]原 = [11111110]反

3. 补码

补码的表示方法是:

(3-1)正数的补码就是其本身;

(3-2)负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1. (即在反码的基础上+1).

[+1] = [00000001]原 = [00000001]反 = [00000001]补  (正数三者相同)

[-1] = [10000001]原 = [11111110]反 = [11111111]补  (负数三者不同
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值