当前位置: 首页 > article >正文

深入理解计算机中的补码、反码、原码

问题:

  • 我们每天用的钟表,其实只有1~12这12个数字,但我们日常会说13点、17点之类的。
    问:13点在钟表上哪个位置?
    答:很简单嘛,1点的位置。
    你不觉得奇怪吗,为啥13点会和1点在同一个位置?换言之,13和1有啥关系,17和5有啥关系?
  • 计算机里为啥要用加法替代减法?
    • 当然是加法电路设计比减法电路设计简单了,加法电路详见CPU之图解算数逻辑单元ALU
  • 怎么用加法替代减法?

一、基本概念

1.0、余数(remainder)

在算术中,当两个整数相除的结果不能以整数商表示时,余数便是其“余留下的量”。当余数为零时,被称为整除。
即:被除数 / 除数 = 商……余数,用数学表示:
a = qd + r, 0 <= r < d,其中a为被除数,q为商(quotient),d为除数(divisor),r为余数(remainder)。
举例:当除数为4时,任何正数除以4时,余数总在0, 1, 2, 3中;

0 / 4 = 0 …… 0
1 / 4 = 0 …… 1
2 / 4 = 0 …… 2
3 / 4 = 1 …… 3
4 / 4 = 1 …… 0
5 / 4 = 1 …… 1
6 / 4 = 1 …… 2
7 / 4 = 1 …… 3
8 / 4 = 2 …… 0
……

如上,被除数从0开始递增时,余数会在0, 1, 2, 3中一直循环下去,这就是同余
在这里插入图片描述
现在我们考虑除数为10时,则余数在0~9这10个数字中出现;把这10个数字同样置于圆盘中,如上图所示。
0-9这10个数字的圆盘中,再也不会有其它数字了,也就是我们限定了我们的数字范围为0~9数字个数(记为模)10
给定圆盘上任一个数字作为
起始点(记为 src)
,任一个数字为 终止点(记为dest),我们考虑从src出发,如何到达dest,可以 前进(记为 + ),可以后退(记为 - )
举例

src = 3,dest = 4;

  • 前进1步(+1),即 3 + 1 = 4;
  • 后退9步(-9), 即 3 - 9 = 4;

src = 3,dest = 3;

  • 前进0步(+0),即 3 + 0 = 3;
  • 后退(9+1)步(-(9+1)), 即 3 - ( 9 + 1) = 4;// 因为我们限定了数字范围为0~9,所以写成(9 + 1)

src = 5,dest = 9;

  • 前进4步(+4),即 5 + 4 = 9;
  • 后退6步(-6), 即 5 - 6 = 9;

通过上面的例子,发现了吗?

  • 对于确定的起止点(如src = 3,dest = 4),不管前进或者后退,我们都可以到达,也就是说前进(1步)等价于后退(9步),
  • 前进步数和后退步数之和为模(10),模意味着啥?
  • 模意味着走了一圈,一个轮回,起点走一圈,又回到了起点

1.1、补数(complement)

定义:对于给定的进位制,相加后能使自然数 a 的位数增加 1 的最小的数。
说人话:就是前面前进和后退的步数等价的组合(如1和9、2和8、3和7、4和6),也即可以组成模(如10)的组合。
即:

当 a = 1时,a只有1位数字(即个位)要想将a变成2(1+1)位数字10(十位、个位),需要增加9。
所以 9是1的补数;当然1也是9的补数,即1、9互为补数;
同理:2、8互为补数;
3、7互为补数;
4、6互为补数;

问: 36的补数是多少?
答:64

根据上面定义,我们知道对于整数a,a的补数 = 模 - a(a>=0);
补数有啥用?
还是回到之前我们0~910个数字的圆盘上来,我们来算小学生都会的10以内的加减法:
在这里插入图片描述
在这里插入图片描述
一顿操作猛如虎,一看结果:直呼OMG,
在这里插入图片描述
正如上图所示:减掉一个数,等价于加上这个数的补数
这里有个重要的前提:数字是有范围的(上面我们限定了0~9这10个数字)

1.2、减法

根据我们上面的结论:当数字是有范围的,则对于这个范围内的数字,减掉一个数,等价于加上这个数的补数,我们可以用加法替代减法,进一步:减去一个正数相当于加上这个正数对应的负数。
还是回到我们前面提到的圆盘上来,我们把这条结论实践下:

  • 我们先在坐标轴上标出0~9这10个数字范围,然后移动这个范围框,使框内正负数个数大致相等,如下图所示,数字范围变成了-5 ~ 4这10个数字。
    在这里插入图片描述
  • 将坐标轴上的数字范围同步映射到圆盘上。
    在这里插入图片描述
  • 此时,圆盘上数字的运算可以用圆盘外相对应的数字进行替代,即圆盘上的数字范围已经改变了,由之前的0 ~ 9 变成了现在的 -5 ~ 4
    在这里插入图片描述

1.3、补码

看到这里,不知道你是否认可前面的推导结论?
不认可?不认可就对了。聪明的你一定发现了上面的计算的问题:
1 - 5 = -4,在我们映射后变成了 1 + 5 = 6-4 不等于 6?虽然圆盘上6确实对应**-4**,但我们需要的是正确的结果-4,怎么将6转换成-4?
在这里插入图片描述

  • 好了,为了后面好叙述,我们先给实际求值结果记为原码,映射后的结果记为补码。我们先分析下实际的求值结果和映射后的结果:
    • 发现原码为0和正数时,补码正确,即原码为0和正数时,原码等于补码
    • 原码为负数时,补码错误,即原码为负数时,原码不等于补码。
      在这里插入图片描述

问题来了?原码为负数时,补码(如6)怎么转换成原码(如-4)?
答: 谜底就在谜面上,你直接看圆盘不就好了,圆盘上都标注了,圆盘内外侧的数字就是我们原码和补码的映射表。
问题又来了?圆盘上的映射怎么来的?
答:通过补数变换得到的,即原码为负数时,补码 + |原码| = 模,也即原码为负数时,补数 + |原码| = 模,在1.1小节中,我们提到了 a的补数 = 模 - a(a>=0);

综上:原码为负数时,补码 + |原码| = 模

二、 二进制里的花花世界

宏观世界搞定了,看看微观世界,十进制搞完了,看看二进制。
二进制我们以1Byte(8bit)研究,1 Byte 可以表示256个数(28),即模为256

2.1 原码

1 Byte 范围的内数字为0~255这256个数字。将其置于圆盘上如下:
在这里插入图片描述

2.2 补码

  • 调整数字范围使其映射到 -128~127256个数字:
    在这里插入图片描述
  • 调整圆盘范围,即原码写到圆盘上,补码写到圆盘外,如下:
    在这里插入图片描述

2.3 补码和原码转换

原码和补码怎么转换呢?
通过1.3节,我们知道如下结论:

  • 原码为0和正数时,原码等于补码
  • 原码为负数时,补码 + |原码| = 模
    在这里插入图片描述
  • 还记得我们在1.3 小节 文末得到的结论吗?原码为负数时,补码 + |原码| = 模,我们变换下这个等式,即原码为负数时,补码 = 模 - |原码|,按照公式我们计算下:
    在这里插入图片描述
    至此,二进制的原码和补码转换我们也已搞定,即:
  • 原码为0和正数时,原码等于补码
  • 原码为负数时,补码 = 模 - |原码|

2.4 反码

不知道你绕晕了吗?还记得我们的初心吗?怎么用加法替代减法?
2.3 节说原码为负数时,补码 = 模 - |原码|,这玩呢?前面这么多推理就是想用加法替代减法,结果搞了半天回到了原点,还使得计算减法,自己替代自己吗?替代了个寂寞……

不急,稍安勿躁,且听下面道来。

2.4.1 模

细想下 :补数的定义,使得当前数的位数增加1位

  • 假设二进制下,当前为8位,问要使得增加的数最小时,怎么才能使得位数增加1位呢,即由原来的8位变成9位?

答:增加的数为1时最小,此时当前8位数达到了最大,那就是0b 1111 1111,即0b 1111 1111 + 0b 0000 0001 = 0b 1 0000 0000

  • 所以我们找到了二进制8位数即将跳变成9位的临界值,即当前8位数的最大值0b 1111 1111
  • 二进制时,一个数变成最大的最快方式,当然是加上该数按位取反后的值。即 0b 1111 1111 - 0b 0000 0001 = 0b 1111 1110,看到了吗,0b 1111 1110正好等于0b 0000 0001按位取反的值,那就叫它反码吧,即原码为负数时,反码为原码数据位按位取反,注意这里是数据位,最高位为符号位“为使得定义一致和完整,我们补充:原码为0和正数时,原码等于反码
    在这里插入图片描述
  • 我们来求下
    在这里插入图片描述
  • 如上图所示,原码为负数时,模 (256) = 8bit最大值 + 1 , 8bit最大值 = 原码的绝对值 + 反码,结合之前的 原码为负数时,补码 = 模 - |原码| 得到:
    在这里插入图片描述
    至此,1 Byte 的数据我们通过补码的形式用加法替代了减法。计算机语言内的整数类型比如java int (4Byte)、long(8Byte) 都是如此。

三、总结

为了使用加法替代加法,我们先后引入了补数、模、原码、补码、反码等概念:

  • 圆盘代表数字是有范围的,该范围的大小即为,并且会首尾相连。计算机1 Byte、4Byte、8Byte天然会限定数据的范围。数据范围限定后,数据一直累计下去一定会产生数据溢出(即进位丢失),从而结束当前轮回,开始下一轮回;
  • 圆盘内的数字是原码,圆盘外的数字是补码,补码也是内的无符号整数。注意,反码 + 1只是求得补码的一种方式,并非补码的定义;
  • 原码为0和正数时,原码等于反码等于补码;
  • 原码为负数时,反码为原码数据位按位取反。

http://www.kler.cn/a/466255.html

相关文章:

  • Spring SpEL表达式由浅入深
  • 【软考网工笔记】计算机基础理论与安全——网络安全
  • 数据结构:树
  • 『SQLite』创建、附加、分离、备份及恢复数据库
  • 『SQLite』详解运算符
  • spring mvc源码学习笔记之五
  • Nginx根据不同ip段配置不同的访问首页
  • 逻辑回归(Logistic Regression) —— 机器学习中的经典分类算法
  • 【Nginx Docker】Office Word Web addin 部署nginx docker
  • 2024转转技术年货发布啦
  • 中建海龙:科技助力福城南产业片区绿色建筑发展
  • HarmonyOS鸿蒙开发 应用开发常见问题总结(持续更新...)
  • Python 链接 Pcomm
  • [CTF/网络安全] 攻防世界 simple_js 解题详析
  • python 插入排序(Insertion Sort)
  • MyBatis一级缓存与二级缓存详解
  • Spring Boot项目中分布式锁实现方案:Redisson
  • Java(四十四)file
  • JavaScript Math(算数) 对象的用法详解
  • 【UE5 C++课程系列笔记】17——DeveloperSettings(开发者设置)的基本使用——读取修改Settings
  • 初步认识UML
  • 动态库dll与静态库lib编程3:DLL导出函数的调用
  • 深度学习笔记10-数据增强(Tensorflow)
  • 在Vue3项目中使用svg-sprite-loader
  • Gitee 的基本用法
  • 查看打开的端口