• 直观的理解计算机中的数值编码


     直观的理解计算机中的数值编码

          

           什么是编码

    大家都知道计算机其实是一个二进制的世界,但是它又是如何为我们呈现出另一面缤纷多彩的世界出来的呢?编码技术就是其中最重要的基础组成部分,事实上在人类世界中编码技术是无处不在的。

    这里所说的编码指并非指CodingCoding指的是书写代码的过程,即编程(Programming)

    首先让我们用“数学”的语言描述一下到底什么是编码:

      编码就相当于一个函数,它将一个集合中的元素映射到另一个集合中去。

     

           通俗的讲,编码就是一个协议(规定),以实现用一个信息来代替另外的一个信息的目的。计算机世界是一个二进制世界,那么她是如何认识‘A’这个字母的呢?答案就是利用编码技术硬性地将二进制数‘1000001’映射为‘A’,只要是遵循这项编码技术的计算机,当它遇到一个字符值等于‘1000001’时,她就会将它解释为字符‘A’。

           一般而言,编码中的两个集合之间的元素都是一一对映的,当然也有例外。

           什么是数值编码

           由于计算机是二进制的,无法表达负数,整数值,小数等数值,因此计算机需要将这些数值进行编码,来扶持这些数值的使用。下面只讲正负整数值的映射。

           原码

        原码是计算机数值编码中最简单的一种,当然也有许多的局限性:

    原码     十进制数

    00000 映射为 0

    00001 映射为 1

    00010 映射为 2

    00011 映射为 3

    00100 映射为 4

    11111 映射为 31

    可以看出,现在上面的原码编码与二进制数是一样的。聪明的童鞋已经看出来了,使用上面的原码不能表示负数,因此我们还是利用编码技术,将第一个二进制数位规定为符号位,当这个位为0时,表示正数;为1时表示负数。如下:

    原码      十进制数

    0 0000 映射为 0

    0 0001 映射为 1

    0 0010 映射为 2

    1 1101 映射为 -13

    1 1110 映射为 -14

    1 1111 映射为 -15

        对于原码编码来说,加法是非常简单的,只要在能够表示的范围内,直接使用二进制加法即可,但是使用减法可就麻烦了(计算机中为了简化设计,使用编码技术来用加法实现减法),例如:

        2 – 13 = -11

        根据交换律,我们可以写成这种形式:

        -13 + 2 = -11

        那么使用原码的加法则为:

        1 1101 + 0 0010

    那么结果就为:

    1 1101 + 0 0010 = 1 1111

    可以看出1 1111在原码中映射为-15,而正确的结果应该是-11。因此当遇到减法时,使用原码是不合适的。为什么不适合呢?我们再观察一下表格,我们可以看出,其中对于负数的编码中,二进制值越大,映射出来的负数却越小:

    1 1101 < 1 1110 < 1 1111

    -13    >-14    > -15

    因此,当我们对-13加上2时,反而得出了-15,而不是-11

    为了解决这个问题,就有了下面的反码编码技术以及补码编码技术。

    反码

    虽然使用原码能够表示负数,但是在运算减法时得出的却是错误的结果。为了满足使用加法实现减法的要求,前辈们创造出了反码这项编码技术(致敬)。反码的映射表是这样子的:

    反码      十进制数

    0 0000 映射为 0

    0 0001 映射为 1

    0 0010 映射为 2

    1 1101 映射为 -2

    1 1110 映射为 -1

    1 1111 映射为 -0

    我们对照表中的每一对相反数可以看出,他们的二进制码都是取反的。例如

    1 1110 -1

    | ||||

    0 0001  1

    将一个二进制数中的位全部置反的操作(0则置为11则置为0),我们称之为取反。在反码的计算机中,对一个数取反,即可得到他的相反数。

    接下来,让我们发挥一点想象力来探究下反码对于减法是如何实现的,我们根据0 00001 1111的顺序列出反码的映射表:

    0    被映射为 0 0000 _____________________正限线 <--------------|

    1    被映射为 0 0001                                                 |

    2    被映射为 0 0010                                                 |

    3    被映射为 0 0011                                                 |

    4    被映射为 0 0100                                                 |

    5    被映射为 0 0101                                                 |

    6    被映射为 0 0110                                                 |

    7    被映射为 0 0111                                                  |

    8    被映射为 0 1000                                                 |

    9    被映射为 0 1001                                                 |

    10   被映射为 0 1010                                                 |

    11   被映射为 0 1011                                                  |

    12   被映射为 0 1100                                                 |

    13   被映射为 0 1101                                                 |

    14   被映射为 0 1110                                                 |

    15   被映射为 0 1111                                                  |

    _________________________________________假想的对称轴            |

    -15   被映射为 1 0000                                                |

    -14   被映射为 1 0001                                                |

    -13   被映射为 1 0010                                                |

    -12   被映射为 1 0011                                                |

    -11   被映射为 1 0100                                                |

    -10   被映射为 1 0101                                                |

    -9    被映射为 1 0110                                                |

    -8    被映射为 1 0111                                                |

    -7    被映射为 1 1000                                                |

    -6    被映射为 1 1001                                                 |

    -5    被映射为 1 1010                                                |

    -4    被映射为 1 1011                                                |

    -3    被映射为 1 1100                                                |

    -2    被映射为 1 1101                                                 |

    -1    被映射为 1 1110                                                |

    -0    被映射为 1 1111_____________________负限线 <--------------|

    我们可以看出,正数跟负数被一条假想的对称轴分开来,相互对称。我们可以看到,反码的编码方式避免了原码所出现的问题:

    -2 <     -1    < -0

    1 1101 < 11110 < 1 1111

    接下来我们看看反码是如何做减法:

    2 – 13 = 2 +(-13) = 0 0010 + 1 0010 = 1 0011

    通过查表可以知道1 0011正是-11

    目前为此,反码的减法似乎已经能够正常工作了,但是接着看下面的式子,情况却有些不同:

    -2 – 13 = -2 + -13 = 1 1101 + 1 0010 = 0 1111

    注意虽然1 1101 + 1 0010应该等于10 1111,但在我们这里的内存模型中,一个数值最多由5个位来储存,因此第6位会被自动丢弃了。

    通过查表可以知道0 1111+15,而不是我们要的值-15。我们接下来比较一下这两种情况,探究一下为什么会出现这种问题?

    第一种情况:2 – 13 = 2 + (-13) = 0 0010 + 10010 = 1 0011

    对于第一种情况,我们可以看到计算机实际上是做了0 0010 + 1 0010这个加法。参照映射表来看,在这个加法中,我们可以把0 0010看成基址,而1 0010是一个偏移,意思就是把0 0010做为原点,向下走1 0010步,也就是说从+20 0010)这个位置,向下走181 0010)步,我们可以看到+2向下的第18个数正是-11,因此答案正确。

    第二种情况:-2 – 13 = -2 + -13 = 1 1101 +10010 = 0 1111

    第二种情况与第一种情况稍有不同。我们可以看到这个操作相当于以-2为原点,向下走18步。但是我们可以看到,当我们走到第三步是就已经越出了负限线,回到正限线从头开始计数,更糟糕的是反码的编码中有两个0,一个+0,一个-0,占了负限线,正限线两个位置,因此我们走的两步实际上都踏在了0这个值上,所以我们距离正确的值就会少走一步。因此,为了得到正确的结果,那么只要我们发现运算时跨越了负限线,那么其值就一定要再加上1(即再向下踏一步)。

    纠正后的操作如下:

    -2 – 13 = -2 + -13 = 1 1101 + 10010 = 0 1111

    0 1111 + 1 = 10000-15

    至此,反码减法完美实现。

    实际上“跨越了负限线”对应的计算机术语是“溢出”,即当前的5位的内存位不足以保存当前的值时(0 0010 + 1 0010的值应该为6位的10 1111),我们称这种情况为“溢出”,当发生溢出时,计算机舍弃第6位(相当于从正限线从新开始向下走步数一样,而不是跑出负限线),并设置溢出位标志为1,随后计算机将溢出位标志的值加到结果值上。

    实际上不管是第一种还是第二种情况,计算机都会将溢价标志的值加到结果值上,结果总是正确的,而不是多加电路来判断是否加上溢出位标志:

    2 - 13 + 溢出位 = 0 0010 + 10010 + 溢出位 = 1 0011 + 0(溢出位) = 10011

    -2 - 13 + 溢出位 = 1 1101 + 10010 + 溢出位 = 0 1111 + 1(溢出位) = 1 0000

           注意,此处一旦计算机做完加法,溢出位就会被置位并与其结果相加。

           补码

           我们可以看到,反码的实现有一个问题,因为它有两个0,而不是一个0,并且这两个0+0-0)是头尾连接在一起的。因此,我们需要进行改进,我们将-0-15都向下位移一个位置,-0则越过负限线与正限线的+0重叠在一起,映身表就变成这样:

    0    被映射为 0 0000 _____________________正限线 <--------------|

    1    被映射为 0 0001                                                  |

    2    被映射为 0 0010                                                 |

    3    被映射为 0 0011                                                 |

    4    被映射为 0 0100                                                 |

    5    被映射为 0 0101                                                  |

    6    被映射为 0 0110                                                 |

    7    被映射为 0 0111                                                 |

    8    被映射为 0 1000                                                 |

    9    被映射为 0 1001                                                  |

    10   被映射为 0 1010                                                 |

    11   被映射为 0 1011                                                 |

    12   被映射为 0 1100                                                 |

    13   被映射为 0 1101                                                 |

    14   被映射为 0 1110                                                 |

    15   被映射为 0 1111                                                 |

    _________________________________________假想的对称轴            |

    -16   被映射为 1 0000                                                |

    -15   被映射为 1 0001                                                |

    -14   被映射为 1 0010                                                |

    -13   被映射为 1 0011                                                 |

    -12   被映射为 1 0100                                                |

    -11   被映射为 1 0101                                                |

    -10   被映射为 1 0110                                                |

    -9    被映射为 1 0111                                                 |

    -8    被映射为 1 1000                                                |

    -7    被映射为 1 1001                                                |

    -6    被映射为 1 1010                                                |

    -5    被映射为 1 1011                                                |

    -4    被映射为 1 1100                                                |

    -3    被映射为 1 1101                                                |

    -2    被映射为 1 1110                                                |

    -1    被映射为 1 1111_____________________负限线 <--------------|

    我们可以看到,在原来的位置(即原来是-15的位置)现在由-16来代替,这样负数就比正数多了一个数,所以补码的正负数是不对称的。

    对于反码来说将一个负数转化为正数的方法是使用取反操作,对于补码来说,也有一个操作将负数转化为正数,我们称之为取补。

    假设有一个数A,其取补操作为:1.A的反码 2.反码值加1

    我们知道反码是从补码改进来的,主要的改动是负数向下位移了一位,因此我们只要求出A的反码,然后加1即可。反码是可以使用取反来将负数转化为正数的,但是补码则不行,因为补码正负数分布是不对称了(补码中0的补码还是0)。当然我们使用逆运算也是可以求出来的,但已经不能叫做补码操作了:

    求补:-A = 求反(+A + 1

    求补逆运算:求反(+A = -A - 1 = +A = 求反((-A-1

    即将负数减1,然后求反即得正数值。

    我们可以使用上面反码中所举的两条式子来验算一下补码的减法:

    第一种情况:2 – 13 = 2 + (-13) = 0 0010 + 1 0011 = 1 0101

           1 0101正是-11的补码,正确。

    第二种情况:-2 – 13 = -2 + -13 = 1 1110 + 10011 = 1 0001

           1 0001正是-15的补码,正确。
          

    到这里我们已经学习了原码、反码以及补码。最后在后面留一道思考题。

     

    思考题:

           在补码中,为什么+3+4等数取补后在负数部分都有对应的位置(即-3-4两个位置),而0取补跟-16逆取补其对应的位置都是它们自身?为什么是-160这两个数而不是其它数?给出一种直观的解释。

     

    对于计算机浮点数编码部分将在另一篇文章中讲叙。

                                                                                                                                                                    

    Thanks for reading, by XLCYun.

                                                                                                                                                       袖里藏云写于2013/10/20凌晨150

     

    希望对计算机学习者们有所帮助,有问题请留言。转载请注明出处。

     

  • 相关阅读:
    Android修改app的图标
    关于Python3中的WARNING: Retrying (Retry(total=4, connect=None, read=None, redirect=None, status=None)) after connection broken by 'ReadTimeoutError("HTTPSConnectionPool(host='pypi.org', port=443):
    Android 7.1 虚拟按键(NavigationBar)源码分析 控件加载、属性控制隐藏
    Android5.1 Browser 闪退分析 /data/tombstones
    RootCommand
    RK3288 Linux4.4.143 适配EETI
    Android 7.1 Camera2 拍照镜像分析
    Docker pull slow resolution
    Dockerfile使用OracleJDK创建自定义tomcat8镜像
    centos使用docker安装ActiveMQ
  • 原文地址:https://www.cnblogs.com/XLCYun/p/3378724.html
Copyright © 2020-2023  润新知