计算机基础(1)——进制转换与表示,原码、反码、补码

进制转换

不同进制用来表示数字的符号

进制 符号
二进制 0 1
八进制 0 1 2 3 4 5 6 7
十进制 0 1 2 3 4 5 6 7 8 9 10
十六进制 0 1 2 3 4 5 6 7 8 9 A B C D E F

几进制就是满几进一

其中十六进制的 A ~ F 在十进制代表的是10 ~ 15,因为要用一个符号代替,所以用了字母

任意进制与十进制

任意进制—>十进制

假设进制数是x

任意进制转换十进制的算法为:从右往左数第n位乘x的n-1次幂然后加起来,下面计算是原式从左往右算,手算的话从右往左写幂数更不容易错

比如说十进制数567,可以表示成:

5102+6101+71005*10^2 + 6*10^1 + 7*10^0

二进制1010101转十进制,计算方式为

126+025+124+023+122+021+120=851*2^6+0*2^5+1*2^4+0*2^3+1*2^2+0*2^1+1*2^0=85

八进制13105640转十进制,计算方式为

187+386+185+084+583+682+481+080=29193281*8^7+3*8^6+1*8^5+0*8^4+5*8^3+6*8^2+4*8^1+0*8^0=2919328

十六进制同理,任意进制都可以这么算

十进制—>其他进制

用十进制数除进制数x得到商和余数,商继续除x得到新的商和余数,继续下去直到商为0,得到的余数就可以组合成对应进制数

十进制数50转二进制

1
2
3
4
5
6
7
8
9
50÷2,商25余0
25÷2,商12余1
12÷2,商 6余0
6÷2,商 3余0
3÷2,商 1余1
1÷2,商 0余1 <-商为0,停止

现在从上到下得到余数010011,实际上的二进制要从下往上倒过来,是110010
所以十进制50的二进制是110010

十进制数50转八进制

1
2
3
4
5
50÷8,商6余2
6÷8,商0余6 <-商为0,停止

现在从上到下得到余数26,实际上的八进制要从下往上倒过来,是62
所以十进制50的八进制是62

十进制数2717转十六进制

1
2
3
4
5
6
2717÷16,商169余13,13对应十六进制为D
169÷16,商 10余9
10÷16,商 0余10,10对应十六进制为A <-商为0,停止

现在从上到下得到余数D9A,实际上的十六进制要从下往上倒过来,是A9D
所以十进制50的十六进制是A9D

其他进制转换

实在不会一步转换就用十进制做跳板罢

二进制与八进制

二进制—>八进制

23=82^3=8

因为2和8的幂次关系,从右往左数每3位二进制可以找转换为一个八进制字符,最后不够的可以补0

二进制 10111001 转八进制:

1
2
3
首先将 10111001 每三个划分一组
10 111 001
然后对每组分别计算

对 10 计算:

121+020=21*2^1 +0*2^0=2

对 111 计算:

122+121+120=71*2^2+1*2^1 +1*2^0=7

对 001 计算:

022+021+120=10*2^2+0*2^1+1*2^0=1

组合起来八进制就是271

二进制 10111001 转八进制为 271

八进制—>二进制

将八进制数的每一位用三个二进制符号表示即可,这里单个数字可以用十进制转二进制时的算法
八进制数512转二进制:

1
2
3
4
5
5 —> 5连除2得到余数逆序为101
1 —> 1连除2得到余数逆序为01,补0至三位即为001
2 —> 2连除2得到余数逆序为10,补0为010

二进制即为101001010

二进制与十六进制

二进制—>十六进制

24=162^4=16

因为2和16的幂次关系,从右往左数每4位二进制可以找转换为一个十六进制字符,最后不够的可以补0

计算过程和二进制转八进制非常相似

二进制 10111001 转十六进制:

1
2
3
首先将 10111001 每四个划分一组
1011 1001
然后对每组分别计算

123+022+121+120=11B1*2^3+0*2^2+1*2^1+1*2^0=11(B)

123+022+021+120=91*2^3+0*2^2+0*2^1+1*2^0=9

二进制 10111001 转十六进制为 B9

十六进制—>二进制

类比八进制转二进制,十六进制每个字符要用四个二进制数表示

十六进制B9A转二进制

1
2
3
4
5
B —> 11连除2,得到余数逆序1011
9 —> 9连除2,得到余数逆序1001
A —> 10连除2,得到余数逆序1010

二进制即为101110011010

八进制和十六进制

用二进制或者十进制做桥梁

原码、反码、补码

计算机通常使用二进制,原码、反码、补码是计算机中对的二进制表示方法

原码:将最高位作为符号位(0表示正,1表示负),其它数字位代表数值本身的绝对值的数字表示方式。

反码:如果是正数,则表示方法和原码一样;如果是负数,符号位不变,其余各位取反,则得到这个数字的反码表示形式。

补码:如果是正数,则表示方法和原码一样;如果是负数,则将数字的反码加上1(相当于将原码数值位取反然后在最低位加1)。

正数的原码、反码、补码完全一样,只有负数需要按照以上规则计算

原码(Sign-Magnitude)

原码是最简单的表示有符号整数的方式。它的规则很简单,即使用二进制表示数值的绝对值,最高位表示符号位(0表示正数,1表示负数)。例如,+5的原码为00000101,-5的原码为10000101。

原码的优点是直观简单,符号位可以直接判断正负。但是原码存在加减法运算时出现的溢出问题。

扩展:原码为什么会有溢出问题

  1. 符号位干扰。两个正数相加可能会因为进位问题导致符号位变化,从而产生错误的负数结果。

    比如5的原码为0101,3的原码为0011,十进制相加后十进制为8,而二进制为1000,对应十进制为-8

  2. 表示范围有限。对于n位二进制数,能够表示的范围是-(2^(n-1)-1)到2^(n-1)-1。如果运算结果超出这个范围,就会发生溢出。

反码(One’s Complement)

为了解决原码的溢出问题,反码被提出。反码的规则是:正数的反码与原码相同,负数的反码是对该数的原码按位取反,将原码中的0变为1,1变为0。

例如,+5的反码仍为00000101,-5的反码为11111010。

反码解决了原码运算溢出的问题,但是存在“0”的两个表示(正0和负0),并且减法运算仍然存在一些问题。

扩展:反码的减法问题

主要问题在于零的表示不唯一。在反码表示法中,零有两种表示形式:正零(所有位为0)和负零(符号位为1,其余位为0)

-5的反码为11111010,7的反码为00000111,7-5也就是7+(-5),得到的二进制结果是00000001(+1)

因为反码中+0是0000000表示的,-0是11111111表示的,-0变为+0需要一个1,所以导致运算结果错误

补码(Two’s Complement)

为了进一步解决反码的问题,补码被引入。补码的规则是:正数的补码与原码相同,负数的补码是对该数的反码加1,这一步解决了反码中少1的问题。

例如,+5的补码仍为00000101,-5的补码为11111011。

总结

通常使用补码来表示二进制负数,结合上述,表示二进制负数方法为:绝对值按位取反(每一位1变0,0变1)后最后一位加1

扩展内容

关于进制转换

小trick:windows计算器中的程序员模式能快速计算不同进制

也有很多在线工具能够快速计算 进制转换 - 在线工具

EXP(C++,二、八、十、十六进制转换)

输入格式:代转换数 当前进制 目标进制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <bits/stdc++.h>
using namespace std;
long long to10(string s,int k){
long long ans=0,d=0;
for(int i=0;i<s.size();i++){
if(s[i]>='A') ans=ans*k+(s[i]-'A'+10);
else ans=ans*k+(s[i]-'0');
d++;
}
return ans;
}
string from10(long long n,int k){
if(n==0) return "0";
string b="0123456789ABCDEF",ans;
while(n>0){
ans+=b[n%k];
n/=k;
}
reverse(ans.begin(),ans.end());
return ans;
}
int main() {
int b,d;
string s;
cin>>s>>b>>d;
cout<<from10(to10(s,b),d);
return 0;
}