补码

从补数标记方式讨论补码起源

平时我们表示数字时,用正负号来指明到底是正号还是负号,如-500到500我们写成:

-500,-499,-498...-4,-3,-2,-1, 0, 1, 2, 3, 4... 498, 499

这种表示方法是以0为原点展开的。如果从一开始就知道要表示的数字范围,因为这些数字的绝对值不会大于500,我们可以采取一种映射的方式,用000-999来表示这些数字:

500,501,502...996,997,998,999,000,001,002,003,004...497,498,499

这些数字与上述1000个数一一对应。这种标记方法就是10的补数。正数范围内的映射关系是不变的,负数需要换算:

如果要计算-255对10的补数,首先用999减255得到744,然后再加1,得到745。

有了补数,我们将无需使用减法,如果要计算143-78,应该首先得到78对10的补数,为922,然后用922+143,去掉溢出后得到65.也就是A-B相当于A+B的补数。

相对我们提到的在10进制中的补数标记方式,在二进制中这种机制被称为2的补数,以8位二进制数为例,从00000000-11111111,对应十进制中的0-255,以1开头的数字都表示负数。此时要计算负数的补数也很简单,例如要计算-125的补数,正数对应二进制为01111101,首先用11111111减该数得到10000010,相当于逐位取反,然后再加1,得到10000011。

而在这种表示方法中,-125对应正数125被表示为01111101,-125的源码就是这个数字的符号位改变成1,也就是11111101,-125的补码是10000011,这个从原码到补码的规律就被总结为:符号位不变,其他各位取反,最后加1.

从0重码问题的解决讨论补码起源

以8位的数据为例,我们怎么才能用8位数据表示有符号数呢?

既然表示有符号,必须能够区分正负,我们不妨用最高位来表示符号,最高位为1表示负数,最高位为0表示正数:

00000000b代表0
10000001b代表-1
11111111b代表-127

但是这种表示方法有一个问题,那就是00000000b和10000000b都代表0,这就是0出现重码问题。

为了解决这个问题,我们先考虑用反码来表示有符号数,也就是00000000-01111111b表示0-127,然后再用它们按位取反后的数据表示负数:

11111110b表示-1
10000000b表示-127

这种用反码表示的方式并没有解决0重码问题,此时11111111b和00000000b都代表0,可见此时11111111b在这种表示方法中是一个不需要的数字,所以我们可以用正数按位取反再加1的方式来表示负数:

11111111b表示-1
10000001b表示-127

这样就赋予了11111111b新的意义,也解决了0重码的问题。

一个比较特别的数字是10000000b,它按位取反再加1是它本身,它代表-128。所以负数能表示的范围比正数多1,这种方式可以表示从-128-127的数字。

补码的意义

补码不仅仅是一种有符号数的表示法,最重要的是它很大程度上简化了计算:A-B相当于A+B的补数。

比如我们计算10-20:

10的补码(就是原码):			00001010b
-20的补码:				11101100b
直接按位相加得	   		        11110110b 就是-10的补码

有了补码后就能大幅度简化减法运算了。

在很多高级语言底层都是用这种方式来进行数据的存储的。

版权声明:本文为yinyunmoyi原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/yinyunmoyi/p/12811428.html