二进制补码的数学原理

博客地址:https://www.cnblogs.com/jackieL/

作者: 梁言  

时间:2019年2月19日

最近在网上查了很多关于补码的文章,要么是长篇大论,要么就是错误百出,所以我用简单的语言把这个问题分析一遍,以便于大家理解记忆,如有错误欢迎留言指正。

一,“原码”、“反码”、“补码”的基本概念

针对还不明白这几个基础概念的同学们需要阐述一下,如果已经知道的同学自行跳过。

1、“原码”

就是二进制定点表示法,即最高位为符号位,“0”表示正,“1”表示负,其余位数表示数字的值。

如:十进制  6 = 二进制原码 0000 0110 

  十进制 -6 = 二进制原码 1000 0110

2、“反码”

正数的反码与其原码相同,负数的反码是对其原码逐位取反,符号位除外。

 如:十进制 6 = 二进制原码 0000 0110 = 二进制反码 0000 0110

   十进制 -6 = 二进制原码 1000 0110 = 二进制反码 1111 1001

3、“补码”

正数的补码与原码相同,负数的补码是在其反码的末位加1。

如:十进制 6 = 二进制原码 0000 0110 = 二进制反码 0000 0110 = 二进制补码 0000 0110

  十进制 -6 = 二进制原码 1000 0110 = 二进制反码 1111 1001 = 二进制补码 1111 1010

二、“原、反、补”的产生原因

因为计算机cpu只有加法运算器,没有减法运算器,所以需要把减法转换为加法来做,所以自然就产生了“原、反、补”来将减法转换为加法计算。

虽然这个东西有点反人类,但毕竟是机器去使用它,我们只需要明白就行。

三、“反码”应运而生

二进制“反码”很容易产生,因为cpu有一个二进制取反逻辑门电路,只要把“原码”通过那个“门”出来就变成“反码”了

“原码”与“反码”有一个特点:

以一个字节二进制数据为例

十进制 -6 = 二进制原码 1000 0110 = 二进制反码 1111 1001

任意一个数的“原码”加上这个数的“反码”=> 10000 0110 + 1111 1001 = 0111 1111 结果是一样的十进制127

=> "原码"  +  “反码” = 127

=> "原码" = 127 -  “反码”

z 为一字节二进制正数(就是需要我们减去的数值),等式两边同时减去z

=>"原码" - z = 127 -  “反码” - z 

=>("原码" - z) = 127 -  (“反码” + z) 

因为 "原码" = 127 -  “反码”

所以(“反码” + z) 为("原码" - z)的“新反码

反之("原码" - z)(“反码” + z)的“新原码

这样就把减法转换为了加法。

如果没有看懂同学不要紧,上图=====>

四、”补码“补漏洞

需要注意的是,当(“反码” + z)的值大于等于127时,计算结果就会出现错误

例如:十进制 8 = 二进制反码 0000 1000

   十进制 6 = 二进制反码 0000 0110

   十进制 -6 = 二进制原码 1000 0110 = 二进制反码 1111 1001

   6 + (- 6) => (反码)0000 0110 + (反码)1111 1001 = (反码)1111 1111 =>(原码)1000 0000 结果为-0,负0在数学上是无定义的

     8 + (-6) => (反码)0000 1000 + (反码)1111 1001 = (反码)0000 0001 =>(原码)0000 0001结果为1,明明8-6结果为2啊,又出错了

    (-6) + (-6) => (反码)1111 1001 + (反码)1111 1001 = (反码)1111 1010 =>(原码)1000 0101结果为-13,明明-6-6结果-12,又出错了

错误是如何产生的了?

因为一个字节二进制的 0000 0000 有8个bit

这个二进制数字的排列数就有2^8个,结果就是256个排列数,256除以2等于128,就是有128种排列用于表示负数,128种排列来表示正数,

(如上图)原反码的圆盘只有127个指针数,跟一字节二进制天然的排列数128周期相比就少一个指针数

这样就导致“原、反码”算法的循环周期和二进制自然的循环周期不一致,不同步而产生错误。

 引入”补码“

求 "原码" + ”补码“?

因为”补码“ = ”反码“ + 1 

=>   "原码" + ”补码“ = "原码" + ”反码“ + 1 

因为 "原码" + ”反码“ = 127

=>  "原码" + ”反码“ + 1 = 127 + 1 = 128 

=> "原码" + ”补码“ = 128

=> "原码"  = 128 -  ”补码“

设 为一字节二进制数(就是需要我们减去的数值),等式两边同时减去z

=> "原码"  - z = 128 -  ”补码“ - z

=> ("原码"  - z) = 128 - (”补码“ + z)

同理(”补码“ + z)是("原码"  - z)的新补码。

这样我们就相当于把圆盘刻度增加一位值(见下图)。

这样算法周期和二进制排列数周期就相匹配了。

需要注意的是:

二进制128个排列是用来表示-128到-1的

二进制128个排列是用来表示0到127的

所以一字节二进制能表示带符号数的范围是-128到127.

规定-128 用 1000 0000(补码) 表示,这个二进制很特殊它减1的效果和它取反的值一样,所以它的原码和补码相等。

-1用 1111 1111(补码)表示。

猜你喜欢

转载自www.cnblogs.com/jackieL/p/10402969.html