/*
按位运算,见 C Primer Plus 中文第六版 第497页
补码的概念:
在补码表示中,最高位为符号位,正数的符号位为0,负数为1
补码的规定如下:
对正数来说,最高位为0,其余各位代表数值本身(以二进制表示),如+42的补码为00101010。
对负数而言,把该数绝对值的补码按位取反,然后对整个数加1,即得该数的补码。如-42的补码为11010110(00101010按位取反11010101+1即11010110)
用补码来表示数,0的补码是单一的,都为00000000。(而在原码,反码表示中,+0和-0的表示是不单一的)。而且可以用111111表示-1的补(这也是补码与原码和反码的区别)。
1、二进制反码或按位取反:~
将1变为0,0变为1,例如:
~(10011010)计算之后得:
(01100101)
2、按位与:&
二元运算符&通过逐位比较两个运算对象,生成一个新值,对于每个相应的位,都为1时,结果才为1
例如:
(10010011)
&(00111101)
-----------------
ans: 00010001
按位与运算符合计算后不再改变的变化规律:a & b & a = b & a & a
3、按位或:|
二元运算符按位或|,通过比较两个运算对象,生成一个新值,对于相应的位存在1,那么值为1
例如:
(10010011)
|(00111101)
-----------------
ans: 10111111
按位或运算符合计算后不再改变的变化规律:a | b | a = b | a | a
4、按位异或:^
二元运算符按位异或^,逐位比较两个运算对象,对于一个相应位,如果只有一个1,那么值为1,否则值为0;
可以理解为:无进位相加,即不考虑进位的时候将对应值相加
例如:
(10010011)
^(00111101)
-----------------
ans: 10101110
异或运算符合交换律和结合律:
a ^ b = b ^ a;
a ^ a = 0;
0 ^ a = a;
a ^ b ^ a = b ^ a ^ a = b;
因此通常用来交换两个变量的值:例如交换a和b的值,我们可以这样计算:
a = a ^ b;
b = a ^ b;
a = b ^ a;
*/
/*
常用算法之提取二进制中最右侧的1:
例如 获取 二进制数 010010101000 中最右侧的1,即获得 000000001000
解法:利用公式 a&(~a+1)即可 公式中的&和~都是按位运算符
a= 010010101000
~a= 101101010111
~a+1= 101101011000
b=a&(~a+1)= 000000001000
因此,按照此规律,可以处理原数的倒数第二个1:
a'=a-b= 010010100000
....周而反复,可以提取每一个1
*/
/*
常用算法之 一个数组中有一种数出现了奇数次,其他数都出现了偶数次,怎么找到这一个数:
例如 112222333344444444555 中5只出现了一次,则利用异或运算的规律,任何数对自己异或运算,结果均为0,即a ^ a = 0;
因此可以通过将所有值进行异或运算,即可获得5为出现奇数次的那个数。
*/
/*
常用算法之 计算一个数字除以2:
只需要对它进行右移一位即可: (a / 2) == (a >> 1); 对正数负数都成立
*/
#include <iostream>
#include <cmath>
using namespace std;
void Bitewise_Negation(); //按位取反运算函数
void Bitewise_EXCLUSIVE_OR();//按位异或^运算函数
void Bitewise_OR(); //按位或|运算函数
void Bitewise_AND(); //按位与&运算函数
int main()
{
Bitewise_EXCLUSIVE_OR();
Bitewise_OR();
Bitewise_AND();
Bitewise_Negation();
return 0;
}
void Bitewise_EXCLUSIVE_OR()//按位异或^运算函数
{
int a = 5;
int b = 6;
/* 我们来看看按位异或^实际运算过程
(00000101) //5的二进制
^(00000110) //6的二进制
-----------------
ans: 00000011 //计算结果为3的二进制
*/
cout << "a = 5,b = 6,则(a ^ b) = " << (a ^ b) << endl;
cout << "a = 5,b = 6,则((a ^ b) ^ a) = " << ((a ^ b) ^ a) << endl;
cout << "a = 5,b = 6,则((b ^ a) ^ a) = " << ((b ^ a) ^ a) << endl;
cout << "a = 5,b = 6,则(b ^ (a ^ a)) = " << (b ^ (a ^ a)) << endl;
cout << "a = 5,b = 6,则((a ^ a) ^ b) = " << ((a ^ a) ^ b) << endl;
cout << "a = 5,b = 6,则(a ^ b ^ a) = " << (b ^ a ^ a) << endl;
cout << "a = 5,b = 6,则(b ^ a ^ a) = " << (b ^ a ^ a) << endl;
cout << "结论:按位异或运算符合分配律:a ^ b ^ a = b ^ a ^ a = b;" << endl;
cout << "=================================" << endl;
}
void Bitewise_OR() //按位或|运算函数
{
int a = 5;
int b = 6;
/* 我们来看看按位或|实际运算过程
(00000101) //5的二进制
|(00000110) //6的二进制
-----------------
ans: 00000111 //计算结果为7的二进制
*/
cout << "a = 5,b = 6,则(a | b) = " << (a | b) << endl;
cout << "a = 5,b = 6,则((a | b) | a) = " << ((a | b) | a) << endl;
cout << "a = 5,b = 6,则((b | a) | a) = " << ((b | a) | a) << endl;
cout << "a = 5,b = 6,则(b | (a | a)) = " << (b | (a | a)) << endl;
cout << "a = 5,b = 6,则((a | a) | b) = " << ((a | a) | b) << endl;
cout << "a = 5,b = 6,则(a | b | a) = " << (b | a | a) << endl;
cout << "a = 5,b = 6,则(b | a | a) = " << (b | a | a) << endl;
cout << "结论:按位或运算符合计算后不再改变的变化规律:a | b | a = b | a | a ;" << endl;
cout << "=================================" << endl;
}
void Bitewise_AND() //按位与&运算函数
{
int a = 5;
int b = 6;
/* 我们来看看按位与&实际运算过程
(00000101) //5的二进制
&(00000110) //6的二进制
-----------------
ans: 00000100 //计算结果为4的二进制
*/
cout << "a = 5,b = 6,则(a & b) = " << (a & b) << endl;
cout << "a = 5,b = 6,则((a & b) & a) = " << ((a & b) & a) << endl;
cout << "a = 5,b = 6,则((b & a) & a) = " << ((b & a) & a) << endl;
cout << "a = 5,b = 6,则(b & (a & a)) = " << (b & (a & a)) << endl;
cout << "a = 5,b = 6,则((a & a) & b) = " << ((a & a) & b) << endl;
cout << "a = 5,b = 6,则(a & b & a) = " << (b & a & a) << endl;
cout << "a = 5,b = 6,则(b & a & a) = " << (b & a & a) << endl;
cout << "结论:按位与运算符合计算后不再改变的变化规律:a & b & a = b & a & a ;" << endl;
cout << "=================================" << endl;
}
void Bitewise_Negation() //按位取反运算函数
{
//int a = 2147483647; // 2^31 - 1 ;
/* 我们来看看按位或|实际运算过程
~(00000101) //5的二进制
-----------------
ans: 11111010 //计算结果为-6的二进制,对负数而言,把该数绝对值的补码按位取反,然后对整个数加1,即得该数的补码。
*/
int sum = 0;
int a = 0;
cout << "sizeof(int) = " << sizeof(int) << endl;
/*
11111111111111111111111111111111 --> 2^30 + 2^29 + ...... + 2^1 + 2^0
*/
for (int i = 0; i < (sizeof(int) * 8 -1); i++)
{
a += pow(2,i);// a = a + pow(2,i);
}
cout << "2^30 + 2^29 + ...... + 2^1 + 2^0 = " << a << endl;
cout << "2^30 + 2^29 + ...... + 2^1 + 2^0 + 1 = " << a + 1<< endl; // a + 1为负数
cout << "经过计算,2147483647 + 1 = 2147483647,则就可以证明:int类型的最大值是2147483647,说明首位是符号位"<< endl;
cout << "(unsigned int)pow(2,50) = " << (unsigned int)pow(2,50) << endl;// 最大值只能获取到2147483648
cout << "(unsigned int)pow(2,32) = " << (unsigned int)pow(2,32) << endl;// 最大值只能获取到2147483648
cout << "(unsigned int)pow(2,31) = " << (unsigned int)pow(2,31) << endl;// 最大值只能获取到2147483648
cout << "(unsigned int)pow(2,30) = " << (unsigned int)pow(2,30) << endl;// 1073741824
/* 我们来看看按位或|实际运算过程
~(01111111111111111111111111111111) //2147483647的二进制
-------------------------------------------------------------------------------------
ans: 10000000000000000000000000000000 //计算结果为-2147483648的二进制:对负数而言,把该数绝对值的补码按位取反,然后对整个数加1,即得该数的补码。
~(00000000000000000000000000000001) //1的二进制
-------------------------------------------------------------------------------------
ans: 11111111111111111111111111111110 //计算结果为-2的二进制:对负数而言,把该数绝对值的补码按位取反,然后对整个数加1,即得该数的补码。
*/
cout << "a = " << a << ",则 ~a = " << ~a << endl;
cout << "a = " << a << ",则 ~a = " << ~~a << endl;
cout << "对0取反:~0 = " << ~0 << endl; // 注意,在计算机中,没有-0
cout << "对1取反:~1 = " << ~1 << endl; //对负数而言,把该数绝对值的补码按位取反,然后对整个数加1,即得该数的补码。
cout << "对2取反:~2 = " << ~2 << endl; //对负数而言,把该数绝对值的补码按位取反,然后对整个数加1,即得该数的补码。
cout << "对3取反:~3 = " << ~3 << endl; //对负数而言,把该数绝对值的补码按位取反,然后对整个数加1,即得该数的补码。
cout << "对3取反:~4 = " << ~4 << endl; //对负数而言,把该数绝对值的补码按位取反,然后对整个数加1,即得该数的补码。
cout << "对3取反:~5 = " << ~5 << endl; //对负数而言,把该数绝对值的补码按位取反,然后对整个数加1,即得该数的补码。
//cout << "=================================" << endl;
}
C/C++学习笔记:按位运算基本知识及用法介绍
于 2021-08-23 23:33:00 首次发布