进制转换以及原、反、补码

可能有的小伙伴不理解了:为啥这节讲这些呢,不应该先把语法什么的学习一下吗?

在之前的文章里面讲过,C语言的学习是需要了解一点计算机工作原理的。可能在初学时期用不到,但是如果真的要深入研究的话,是绝对避免不了的。况且这节的内容其实比较简单,我们不如先把它了解了,方便日后我们的进阶学习。

学习编程,尤其是底层编程,进制转换是一个避免不了的话题。其实我们仔细去观察,发现其实是有不少规律可循的。


进制转换

  1. 二进制 -> 十进制

    这个方法就四个字:按权相加

    数学公式来表示就是:

    i=0Mkni;{kin(2)M\sum^{M}_{i=0}{\color{CadetBlue} k}\cdot {\color{red}n}^i;\quad \begin{cases} \color{CadetBlue}k为位权i上对应的数字\\ \color{red}n为进制所对应的数字_{\left(如二进制为2\right)}\\ M为此数的最高位权 \end{cases}

    我们先来了解下位权的概念,举个栗子你就明白啦:

    二进制数 00101011
    对应位权 76543210

    于是就有了方法:

    十进制 = 依次将每个二进制位的值乘以2的位权次方再相加

    00101011(2)00101011_{(2)}

=027+026+125+024+123+022+121+120=42\begin{aligned} &= 0*2^7 + 0*2^6 + 1*2^5 + 0*2^4 + 1*2^3 + 0*2^2 + 1*2^1 + 1*2^0\\ &= 42 \end{aligned}

另外说明一下:按权相加适用于任何进制的数到十进制的数的转换哟!

  1. 二进制(补码) -> 十进制

    如果符号位为0,那就和上面一种情况一样。

    如果符号位为1,则此时符号位的位权不变,但该位的权值应该乘以**-1**,如:

    10111100(2)10111100_{(2)}

=127+026+125+124+123+122+021+020=68\begin{aligned} &= -1*2^{7} + 0*2^{6} + 1*2^{5} + 1*2^{4} + 1*2^{3} + 1*2^{2} + 0*2^{1} + 0*2^{0}\\ &= -68 \end{aligned}

大家可能看懵了,啥是符号位补码又是啥?别急,后面且听我娓娓道来。

  1. 十进制 -> 二进制

    这好像大家高中的时候学过吧,这里我总结下:

    将待转换的十进制数不断地除以2,直到商为零,然后将每次除得的余数倒序拼凑即可。

  2. 二进制 <-> 八进制

    这个很简单,只需要把数字从0位权开始三个一组分开,然后分别转化成八进制数再拼接起来即可。这里提供个表格,方便不想转换的同学查表。

    二进制 八进制
    000 0
    001 1
    010 2
    011 3
    100 4
    101 5
    110 6
    111 7
  3. 二进制 <-> 十六进制

    这个就更简单了,只需要记住如下的表格,然后将二进制数从0位权开始四个一组分开,分别对应后再重新拼凑即可。

    二进制 十六进制
    0000 0
    0001 1
    0010 2
    0011 3
    0100 4
    0101 5
    0110 6
    0111 7
    1000 8
    1001 9
    1010 A
    1011 B
    1100 C
    1101 D
    1110 E
    1111 F

原码

在了解这些码之前呢,我们先来了解一下基本知识。

首先,我们要知道,计算机中,存放一个数据最小的单位是bit(位,也称比特),而在TCP/IP协议中,规定1Byte = 8bit,Byte(字节)是最小的容量单位。因此,一个字节里面就有8个位。那么如果使用一个字节来表示数字的话,就是282^8个数。那么为了表示数字的符号,我们又规定,一个字节中的最高位(也就是最左边的那一位)是符号位,符号位为0表示的是非负数(记住,是正数和0),1则表示的是负数

原码,实际上就是将十进制数转化成二进制的形式,然后把符号位加上即可。比如说5的原码,先转化为二进制,即101,再填充至7位,即000 0101,然后因为是非负数,符号位为0,那么5的原码就是0000 0101

明白了吧,是不是觉得特别简单呢?那么-5的原码又是啥呢?

聪明的你一定想到了,就直接把符号位变为1就行啦,即1000 0101

简直完美啊,如此简洁地表达了数字在存储器中的表示方式。直观形象又好记!

But!现在该说但是了。请问5和-5相加等于多少呢?

0啊!当然是0!这难道还要思考吗??

是的,没错,但是你用两个数的原码加起来看看等于几?

0000,0101+1000,0101=1000,10100000,0101 + 1000,0101 = 1000,1010

咦?咋就不等于0了呢……

没错,问题就在这。如果用这种所谓的直观的形式去表达,既有悖于我们的常识,又不方便计算机的电路设计,于是我们的先人们开始了探索。

反码

我们从上面的例子了解到,负数在表达的时候,不能这么图方便,不然可能会造成一些冲突和错误。先人们就开始折腾。结果弄出来反码这个东西。

我们说了,只是负数的表达除了问题,所以正数无论是原码,反码还是后面我们讲的补码,都是不变的。要变的只有负数。

负数原码转反码的方法很简单,符号位单独不变,只需要把其他的按位取反即可。什么叫按位取反呢,说成人话,就是0变1,1变0。还是拿刚刚的-5作为栗子。-5的原码为

1000 0101

那么转化为反码,符号位不变,其余按位取反:

1111 1010

懂了吧,那么这样5和-5相加会得到什么呢?

0000,0101+1111,1010=1111,11110000,0101 + 1111,1010 = 1111,1111

我们发现,得到了一个8bit能表示的最大的值。这个路子能否成功,就看临门一脚了。距离成功仅一步之遥。

补码

这个“临门一脚”就交给补码啦。补码的出现,相反数相加不为0的情况才被彻底解决。那么补码又是个啥东东呢?

正数的补码还是原码,这个刚刚说过了。而负数的补码,则是在反码的基础上加个1。可为啥是1呢?上面那个反码的例子,我相信大家是有些想法了,如果给1111 1111这个数加个1,那么等于多少呢?

答案是1 0000 0000

又的小伙伴可能会抬杠了,那还是不等于0呀。我们注意观察,这个数已经达到9位了,最高位已经溢出一个Byte,那么对于一个Byte所表示的数字,计算机可以将最高位直接舍弃,bingo!不就变成0000 0000了吗?

是不是很奇妙呢?不得不说,先人们着实厉害呀,在这里向他们致敬!

另外,还要提醒一点,就是补码的补码就是原码,至于为什么大家可以回去自己试试,这里就不多赘述咯('')

最后,在这篇文章结束前,我还想提醒一句:**正数是三码归一!!**别到时候别人给你一个正数叫你转换,你还按位取反啥啥啥的,那样就丢人啦!

今天的博客就到此结束啦,如果你喜欢笔者的文章,记得点个赞哦(≧∇≦)ノ