NO.36十六届蓝桥杯备战|位运算和操作符属性|进制转换|原码反码补码|左移|右移|按位与|按位或|按位异或|按位取反(C++)
操作符的分类
分类 | 操作符 |
---|---|
算术操作符 | +、-、* 、/、% |
移位操作符 | <<、>> |
位操作符 | &、|、^ |
赋值操作符 | =、+=、-=、*= 、/=、%=、<<=、>>=、&=、|=、^= |
单⽬操作符 | !、++、–、&、* 、+、-、~、sizeof、(类型) |
关系操作符 | >、>=、<、<=、== 、!= |
逻辑操作符 | &&、| |
条件操作符 | ? : |
下标引⽤ | [] |
函数调⽤ | () |
⼆进制和进制转换
15 的 2 进制:1111
15 的 8 进制:17
15 的 10 进制:15
15 的 16 进制:F
//16进制的数值之前写:0x
//8进制的数值之前写:0
⼆进制
⾸先我们还是得从 10 进制讲起,其实 10 进制是我们⽣活中经常使⽤的,我们已经形成了很多常识:
• 10 进制中满 10 进 1
• 10 进制的数字是由 0~9 的数字组成的
其实⼆进制也是⼀样的
• 2 进制中满 2 进 1
• 2 进制的数字是由 0~1 的数字组成的
那么 1101 就是⼆进制的数字了。
2进制转10进制
其实 10 进制的 123 表⽰的值是⼀百⼆⼗三,为什么是这个值呢?其实 10 进制的每⼀位是有权重的, 10 进制的数字从右向左是个位、⼗位、百位…,分别每⼀位的权重是 1 0 0 , 1 0 1 , 1 0 2 … 10^0,10^1,10^2\dots 100,101,102…
百位 | 十位 | 个位 | ||
---|---|---|---|---|
10进制的位 | 1 | 2 | 3 | |
权重 | 10^2 | 10^1 | 10^0 | |
权重值 | 100 | 10 | 1 | |
求值 | 1*100 | 2*10 | 3*1 | 123 |
2 进制和 10 进制是类似的,只不过 2 进制的每⼀位的权重,从右向左是
2
0
,
2
1
,
2
2
…
2^0,2^1,2^2\dots
20,21,22…
1101
2进制的位 | 1 | 1 | 0 | 1 | |
---|---|---|---|---|---|
权重 | 2^3 | 2^2 | 2^1 | 2^0 | |
权重值 | 8 | 4 | 2 | 1 | |
求值 | 1*8 | 1*4 | 0*2 | 1*1 | 13 |
8进制数转10进制数,⽅法类似,只是权重是
8
0
8^0
80 开始,然后依次是:
8
0
,
8
1
,
8
2
…
8^0,8^1,8^2\dots
80,81,82…
16进制数转10进制数,⽅法类似,只是权重是
1
6
0
16^0
160 开始,然后依次是:
1
6
0
,
1
6
1
,
1
6
2
…
16^0,16^1,16^2\dots
160,161,162…
10进制转2进制数字
2进制转8进制
8 进制的数字每⼀位是 0~7 中的数字, 0~7 中的数字,各⾃写成 2 进制,最多有 3 个 2 进制位就⾜够了,⽐如 7 的⼆进制是 111 ,所以在 2 进制转 8 进制数的时候,从 2 进制序列中右边低位开始向左每 3 个 2 进制位会换算⼀个 8 进制位,剩余不够 3 个 2 进制位的直接换算。
如: 2 进制的 01101011 ,换成 8 进制: 0153 , 0 开头的数字,会被当做 8 进制
8进制数转换2进制数是相关的过程,每⼀个8进制位转换成3个2进制位就⾏
2进制转16进制
16 进制的数字每⼀位是 0~9 , a~f 的, 0~9 , a~f 的数字,各⾃写成 2 进制,最多有 4 个 2 进制位就⾜够了,⽐如 f 的⼆进制是 1111 ,所以在 2 进制转 16 进制数的时候,从 2 进制序列中右边低位开始向左每 4 个 2 进制位会换算⼀个 16 进制位,剩余不够 4 个⼆进制位的直接换算。
如: 2 进制的 01101011 ,换成 16 进制: 0x6b , 16 进制表⽰的时候前⾯加 0x 。
16进制数转换2进制数是相关的过程,每⼀个16进制位转换成3个2进制位就⾏
原码、反码、补码
其实说到 2 进制,在计算机内部,数值是以 2 进制的形式来进⾏表⽰和存储。后⾯要讲解的位运算的操作符就是针对整数的⼆进制来进⾏运算的
移位运算符:>> <<
位运算符 : & | ^
整数的 2 进制表⽰⽅法有三种,即原码、反码和补码;整数分为有符号整数(signed)和⽆符号整数(unsigned)。
有符号整数的原码、反码和补码的⼆进制表⽰中均由符号位和数值位两部分组成, 2 进制序列中,最⾼位的 1 位是被当做符号位,剩余的都是数值位,符号位都是⽤ 0 表⽰“正”,⽤ 1 表⽰“负”。
正整数的原、反、补码都相同。
负整数的三种表⽰⽅法各不相同,需要计算。
原码:直接将数值按照正负数的形式翻译成⼆进制得到的就是原码。
反码:将原码的符号位不变,其他位依次按位取反就可以得到反码。
补码:反码 +1 就得到补码。
由补码得到原码也是可以使⽤:取反, +1 的操作。
int a = -10;
原码:10000000 00000000 00000000 00001010
反码:11111111 11111111 11111111 11110101
补码:11111111 11111111 11111111 11110110
int a = 10;
原码:10000000 00000000 00000000 00001010
反码:10000000 00000000 00000000 00001010
补码:10000000 00000000 00000000 00001010
⽆符号整数的三种 2 进制表⽰相同,没有符号位,每⼀位都是数值位。
整数在内存中是以补码的形式存储的,整数在参与位运算的时候,也都是使⽤内存中的补码进⾏计算的,计算的产⽣的结果也是补码,需要转换成原码才是真实值。
在计算机系统中,整数的数值⼀律⽤补码来表⽰和存储。原因在于,使⽤补码,可以将符号位和数值域统⼀处理;同时,加法和减法也可以统⼀处理(CPU只有加法器)此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路
练习
B3619 10 进制转 x 进制 - 洛谷
#include <bits/stdc++.h>
using namespace std;
string s = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
void n_to_x(int n, int x)
{
if (n >= x)
n_to_x(n / x, x);
cout << s[n % x];
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int n, x;
cin >> n >> x;
n_to_x(n, x);
return 0;
}
B3620 x 进制转 10 进制 - 洛谷
#include <bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int x;
string s;
cin >> x >> s;
size_t n = s.size();
int j = 0;
int sum = 0;
for (int i = n-1; i >= 0; i--)
{
if(s[i] <= '9')
sum += (s[i] - '0') * pow(x, j);
else
sum += (s[i] - 'A' + 10) * pow(x, j);
j++;
}
cout << sum << endl;
return 0;
}
#include <bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int x;
string s;
cin >> x >> s;
int ret = stoi(s, NULL, x);
cout << ret << endl;
return 0;
}
P1143 进制转换 - 洛谷
#include <bits/stdc++.h>
using namespace std;
string x = "0123456789ABCDEF";
void n_to_m(int t, int m)
{
if (t >= m)
n_to_m(t / m, m);
cout << x[t % m];
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int n, m;
string s;
cin >> n >> s >> m;
int t = stoi(s, NULL, n);
n_to_m(t, m);
return 0;
}
位运算操作符
<< //左移操作符
>> //右移操作符
& //按位与操作符
| //按位或操作符
^ //按位异或操作符
~ //按位取反操作符
左移操作符
左移操作符是双⽬操作符,形式如下:
int num = 10;
num << i; //将num的⼆进制表⽰,左移i位
#include <iostream>
using namespace std;
int main()
{
int num = 10;
int n = num << 1;
cout << "n = " << n << endl;
cout << "num = " << num << endl;
return 0;
}
右移操作符
右移操作符是双⽬操作符,形式如下:
num >> i;//将num的⼆进制表⽰右移i位
#include <iostream>
using namespace std;
int main()
{
int num = -1;
int n = num >> 1;
cout << "n = " << n << endl;
cout << "num = " << num << endl;
return 0;
}
右移运算分两种:逻辑右移和算术右移,具体采⽤哪种右移⽅式取决于编译器,⼤部分的编译器采⽤的是算术右移。两种移位⽅式的规则如下:
- 逻辑右移:左边⽤ 0 填充,右边丢弃
- 算术右移:左边⽤原该值的符号位填充,右边丢弃
逻辑右移
算数右移
对于移位运算符,不要移动负数位,这个是标准未定义的
按位与操作符
按位与操作符是双⽬操作符,形式如下:
a & b; //a和b按位与运算
对两个数的对应⼆进制位进⾏与运算,只有对应的两个⼆进位都为 1 时,结果才为 1
#include <iostream>
using namespace std;
int main()
{
int a = -5;
int b = 7;
int c = a & b;
cout << c << endl;
return 0;
}
-5的原码: 10000000 00000000 00000000 00000101
-5的反码: 11111111 11111111 11111111 11111010
-5的补码: 11111111 11111111 11111111 11111011
7的补码:00000000 00000000 00000000 00000111
-5的补码和7的补码按位与运算如下
计算得到⼆进制是: 00000000 00000000 00000000 00000011 ,这个⼆进制序列当作补码时,且最⾼是 1 ,说明是正数,所以原码也是这个,所以最终 c 为 3
按位或操作符
按位或操作符是双⽬操作符,形式如下
a | b; //a和b按位或运算
对两个数的对应⼆进制位进⾏或运算,对应的两个⼆进位只要有 1 ,或结果就为 1
#include <iostream>
using namespace std;
int main()
{
int a = -5;
int b = 7;
int c = a | b;
cout << c << endl;
return 0;
}
-5的原码: 10000000 00000000 00000000 00000101
-5的反码: 11111111 11111111 11111111 11111010
-5的补码: 11111111 11111111 11111111 11111011
7的补码:00000000 00000000 00000000 00000111
-5的补码和7的补码按位或运算如下:
计算得到⼆进制是: 11111111 11111111 11111111 11111111 ,这个⼆进制序列当作补码
时,且最⾼是 1 ,说明是负数,求的原码为: 10000000 00000000 00000000 00000001 ,所以最终c为 -1 。
按位异或操作符
按位异或操作符是双⽬操作符,形式如下:
a ^ b; //a和b按位异或运算
对两个数的对应⼆进制位进⾏异或运算,对应的两个⼆进位相同则为 0 ,相异则为 1
#include <iostream>
using namespace std;
int main()
{
int a = -5;
int b = 7;
int c = a ^ b;
cout << c << endl;
return 0;
}
-5的原码: 10000000 00000000 00000000 00000101
-5的反码: 11111111 11111111 11111111 11111010
-5的补码: 11111111 11111111 11111111 11111011
7的补码:00000000 00000000 00000000 00000111
-5的补码和7的补码按位异或运算如下:
计算得到⼆进制是: 11111111 11111111 11111111 11111100 ,这个⼆进制序列当作补码
时,且最⾼是 1 ,说明是负数,求的原码为: 10000000 00000000 00000000 00000100 ,所以最终 c 为 -4
按位取反操作符
按位取反操作符是单⽬操作符,形式如下:
~a; //对a的⼆进制位按位取反
对操作数的⼆进制位进⾏按位取反运算, 2 进制是 0 的变成 1 ,是 1 的变成 0
#include <iostream>
using namespace std;
int main()
{
int a = -5;
int b = ~a;
cout << b << endl;
return 0;
}
-5的原码: 10000000 00000000 00000000 00000101
-5的反码: 11111111 11111111 11111111 11111010
-5的补码: 11111111 11111111 11111111 11111011
对-5的补码按位取反运算如下:
计算得到⼆进制是: 00000000 00000000 00000000 00000100 ,这个⼆进制序列当作补码时,且最⾼是 0 ,说明是正数,所以原码也是这个,最终 c 为 -4