博客地址: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
=> "原码" - z = 128 - ”补码“ - z
=> ("原码" - z) = 128 - (”补码“ + z)
同理(”补码“ + z)是("原码" - z)的新补码。
这样我们就相当于把圆盘刻度增加一位值(见下图)。
这样算法周期和二进制排列数周期就相匹配了。
需要注意的是:
二进制128个排列是用来表示-128到-1的
二进制128个排列是用来表示0到127的
所以一字节二进制能表示带符号数的范围是-128到127.
规定-128 用 1000 0000(补码) 表示,这个二进制很特殊它减1的效果和它取反的值一样,所以它的原码和补码相等。
-1用 1111 1111(补码)表示。
原文:https://www.cnblogs.com/jackieL/p/10402969.html