博客地址:https://www.cnblogs.com/jackieL/算法
做者: 梁言 spa
时间:2019年2月19日指针
最近在网上查了不少关于补码的文章,要么是长篇大论,要么就是错误百出,因此我用简单的语言把这个问题分析一遍,以便于你们理解记忆,若有错误欢迎留言指正。blog
一,“原码”、“反码”、“补码”的基本概念同步
针对还不明白这几个基础概念的同窗们须要阐述一下,若是已经知道的同窗自行跳过。博客
一、“原码”数学
就是二进制定点表示法,即最高位为符号位,“0”表示正,“1”表示负,其他位数表示数字的值。it
如:十进制 6 = 二进制原码 0000 0110 基础
十进制 -6 = 二进制原码 1000 0110循环
二、“反码”
正数的反码与其原码相同,负数的反码是对其原码逐位取反,符号位除外。
如:十进制 6 = 二进制原码 0000 0110 = 二进制反码 0000 0110
十进制 -6 = 二进制原码 1000 0110 = 二进制反码 1111 1001
三、“补码”
正数的补码与原码相同,负数的补码是在其反码的末位加1。
如:十进制 6 = 二进制原码 0000 0110 = 二进制反码 0000 0110 = 二进制补码 0000 0110
十进制 -6 = 二进制原码 1000 0110 = 二进制反码 1111 1001 = 二进制补码 1111 1010
2、“原、反、补”的产生缘由
由于计算机cpu只有加法运算器,没有减法运算器,因此须要把减法转换为加法来作,因此天然就产生了“原、反、补”来将减法转换为加法计算。
虽然这个东西有点反人类,但毕竟是机器去使用它,咱们只须要明白就行。
3、“反码”应运而生
二进制“反码”很容易产生,由于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)的“新原码”
这样就把减法转换为了加法。
若是没有看懂同窗没关系,上图=====>
4、”补码“补漏洞
须要注意的是,当(“反码” + 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(补码)表示。