整数二进制补码的数学原理(two's complement)

2023-05-16

转载自整数二进制补码的数学原理(two's complement)

=====================================================================================================================================

最近重新学习CPU体系结构,对使用二进制补码原理来消除带符号数和无符号数计算差异,以及整合减法运算器到加法运算器,从而简化CPU硬件设计的原理很感兴趣,所以特地思考了下,查看了一些网上关于two's complement的文章,但大部分还是太过学术,经过整理,我想以一种比较简洁的方式表达出来。为了简单起见,我使用了4位字长的寄存器作为例子,32位和64位道理一样。想了解补码更为科学的数学原理可以参考wikipedia关于one's complement、two's complement的相关文章。

硬件设计以简洁为目标,所以整数的运算最好只有加法,而且不用对符号位进行特殊处理,能达到这个目标吗?当然可以,那就是使用补码(two's complement),所谓补码其实是针对带符号数来说的,其意思就是正数使用原码,而负数使用2的字长的指数减负数的绝对值表示,即x = pow(2, word_length) - abs(x),这个补码的简单计算方法就是我们计算机书中常说的,将x绝对值取反加1。现在你知道补码的真正计算方法了吧。为什么要将负数表示成这样呢?这是有数学原理的,这正是本文需要阐释的内容,充分了解后对CPU常用指令编程就打下了坚实的基础(general purpose instructions都是针对整数的),以后可能还会增加关于浮点数计算规范的文章。

现在我们来看一个减法:
7 - 6     (式1)
能把它变成一个加法吗?我来试试:
7 + (16 - 6)- 16    (式2)
16是4位寄存器最小的溢出数(2**4 **表示pow运算),以上两个式子是完全等价的,在我们看来比较繁琐的第二个式子却正是CPU内部整数计算单元所采用的方法,由于一些特殊原因,CPU只需要计算第一个加法,其余两个减法分别由编译器、人或寄存器自动截断完成了。
经过前面的叙述,我们知道了16 - 6就是-6在4位字长机器上的补码,这步计算一般是编译器完成的,将负数直接存储为补码形式,这里是1010。我们来看看CPU如何计算:
  0 1 1 1        (7)
+ 1 0 1 0     (10)
-----------   ----------
1 0 0 0 1     (17)

以上式子完成了式(2)中前两步计算,还需要减16才能得到正确结果,神奇的地方到了,因为机器是4位字长,所以第五位1直接丢弃掉了,就是溢出,这相当于自动减了16,所以最后结果就是0001,等于1,完成了式2个所有计算,得到了正确结果,现在你应该明白了为什么会选择最小溢出数所为补码转换中的被减数了吧,就是为了完成自动溢出,从而实现最后的减法。

再来看看2个负数相加,看看CPU是如何把它们当纯粹二进制运算而结果却丝毫不差的:
(-6) + (-7)  (式3)
依据上面的规律换成如下式子
[(16 - 6) - 16] + [(16 - 7) - 16]    (式4)
其中(16 - 6)和(16 - 7)部分已经由编译器完成,就是对应负数的补码,让我们来看看CPU的计算内容:
  1 0 1 0     (10)
+ 1 0 0 1      (9)
-----------   ------
1 0 0 1 1      (19)

式4中还需要减2个16,这里第5位已经自动溢出减了一个16,我们还要减一个16才能得到正确结果,可是寄存器中结果0011,光凭这个结果,我不知道这到底是最终值还是还需要减16,这可太糟糕了,产生这个问题的原因是如果使用全部4位寄存器存储值时,会产生带符号数二进制歧义问题,打个比方,-9用二进制补码表示是(16 - 9),二进制为0111,居然和整数的7是一样的,光凭这串二进制我无法知道它是-9还是7,好吧,我确实聪明,想到了一个办法,嘿嘿,让我们来看看4位寄存器能存储的二进制有哪些:
0 0 0 0
0 0 0 1
0 0 1 0
0 0 1 1
0 1 0 0
0 1 0 1
0 1 1 0
0 1 1 1
1 0 0 0
1 0 0 1
1 0 1 0
1 0 1 1
1 1 0 0
1 1 0 1
1 1 1 0
1 1 1 1
我可以将最高位数字当作解释数字符号的标志,如果是0我就当正数解释,如果是1我就当负数解释,当正数解释后不用再减16,直接就是最终结果,而如果是负数则还需要减个16才是最终结果,因为我们是用16-x来表示-x的,正好正数负数对半(假设0是正数),再回到上面那个问题,(-6) + (-7)CPU寄存器中最终为0011,我当应该当正数解释,正数不用减16,所以最后等于3,不对!应该是-13,还需要减个16才对,可我们刚说了正数不用减。到底哪里出问题了?大家思考下。
原来如果我们将二进制用以上阐述的方式解释,决定了4位二进制表示的数的范围只能是[-8~7],实际上寄存器如果从左端溢出的话,其值是在[...0\1\2\3\4\5\6\7\-8\-7\-6\-5\-4\-3\-2\-1\0...]不断循环的,也就是说刚才的-13从-8向左数5位,又循环回到了3,我们必须有办法判断溢出情况,如果我们把寄存器中的二进制当无符号数解释,那很简单,只要最高为产生进位那就溢出了,可如果当带符号数解释,如何看最后的值是否溢出呢?
这是个补码数学原理的精髓所在,有了这个推理,CPU才能做到同等处理带符号数和无符号数,我们来仔细分析下数学上的原理,在CPU看来寄存器中的就是纯粹的二进制,相当于无符号数,如果两个数相加时,最高位产生了进位,则表示结果肯定位于[16~30],如果次高位向最高位产生了进位则表示结果低3位相加结果范围位于[8~14],由于最高位溢出被丢弃,表示对最终结果减了16,而次高位向最高位产生进位,表示最终结果最小为8,现在就有如下几种结论:
(1)最高位有进位,次高位有进位,则最终结果位于[-8~6]
(2)最高位有进位,次高位无进位,则最终结果位于[-16~-9]
(3)最高位无进位,次高位有进位,则最终结果位于[8~14]
(4)最高位无进位,次高位无进位,则最终结果位于[0~7]
而我们带符号的解释方法,决定了数的范围为[-8~7],怎么样一眼就看出该如何判断带符号数计算是否溢出了吧!

然我们来看看CPU EFLAGS寄存器中最常用的6个标志位CF,PF,AF,ZF,SF,OF,我只解释CF和OF,其余的4个都很好理解,CF表示两个操作数进行二进制整数计算时最高位发生了进位,很显然可以用来判断无符号数是否溢出,而OF是寄存器次高位是否向最高位发生进位的标志(进位1,否则为0)与CF位的XOR值,是不是很神奇,就是我们最后阐述的四项规则,正好用来判断带符号整数是否溢出。

是不是无法想象在一般书上一笔带过的整数计算用的补码规范后面却隐藏了这么多原理,正是这些特性,决定了处理器设计时采用二进制补码进行整数计算,他使带符号数和无符号数的加减运算全部用无符号数加法运算实现,使电路实现大为简化,增加了处理器效率,减少了设计制造成本。但整数的乘法/除法运算却无法这样处理,这就是为什么有带符号的乘除指令而加法和减法却没有,从一定意义上讲,其实减法指令只是加法指令的一个包装,因为CPU内部没有减法逻辑,只有加法。


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

整数二进制补码的数学原理(two's complement) 的相关文章

  • On make and cmake

    你或许听过好几种 Make 工具 xff0c 例如 GNU Make xff0c QT 的 qmake xff0c 微软的MS nmake xff0c BSD Make xff08 pmake xff09 xff0c Makepp xff0
  • 制作html css 步骤进度条(完整代码)

    这个动画步骤进度条的css制作的非常简单 那里有两个按钮可以控制步骤 xff0c 它们将逐步进行 我在这个多步骤进度条 css 中使用了 4 个步骤 如果你愿意 xff0c 你可以使用更多 我使用了一些 javascript 来创建这一步进
  • 搜狗语料库word2vec获取词向量

    一 中文语料库 本文采用的是搜狗实验室的搜狗新闻语料库 xff0c 数据链接 http www sogou com labs resource cs php 首先对搜狗语料库的样例文件进行分析 搜狗语料库由搜狗实验室提供 xff0c 我们使
  • c++堆排序原理和实现

    堆排序 xff0c C 43 43 实现 堆是一种特殊的树形数据结构 xff0c 即完全二叉树 堆分为大根堆和小根堆 xff0c 大根堆为根节点的值大于两个子节点的值 xff1b 小根堆为根节点的值小于两个子节点的值 xff0c 同时根节点
  • TCP流量控制和拥塞控制

    先来了解2个TCP的概念 xff1a MSS xff1a Maximum Segment Size xff0c TCP一次传输发送的最大数据段长度 RTT xff1a Round Trip Time xff0c 往返时延 xff0c 表示从
  • c++static关键字的作用

    c 43 43 static关键字的作用 c c 43 43 共有 1 xff09 xff1a 修饰全局变量时 xff0c 表明一个全局变量只对定义在同一文件中的函数可见 2 xff09 xff1a 修饰局部变量时 xff0c 表明该变量的

随机推荐

  • git合并解决

    远程分支被修改了 xff0c 本地分支落后修改 xff0c 合并 方法一 xff1a 在你自己的分支上 xff0c 如果有本地修改先 git stash git pull git merge origin master 如果本地分支是mas
  • TCP Keepalive

    TCP Keepalive的起源 TCP协议中有长连接和短连接之分 短连接环境下 xff0c 数据交互完毕后 xff0c 主动释放连接 xff1b 长连接的环境下 xff0c 进行一次数据交互后 xff0c 很长一段时间内无数据交互时 xf
  • 【转载】深入浅出讲解FOC算法与SVPWM技术——自制FOC驱动器

    原文链接 xff1a https zhuanlan zhihu com p 147659820 参考文献 xff1a https zhuanlan zhihu com p 364247816 https www zhihu com ques
  • Linux 基本用户和组命令

    Linux 基本用户和组命令 1有关用户的命令 1 新增用户 Useradd 43 用户名 2 查看用户是否存在 id 43 用户名 3 删除用户 sudo userdel 用户名 只会删除用户本身 sudo userdel r 43 用户
  • Linux文件及权限

    Linux文件及权限 1 xff0e 查看文件权限 1 ls l 命令 ll 命令 显示详细信息 例 xff1a root 64 localhost Desktop ll total 178752 rwxr xr x 1 root root
  • 各种排序算法和应用场景

    简介 插入排序 插入排序是一种较为简单的排序算法 xff0c 它的基本思想是通过构建有序序列 xff0c 对于未排序数据 xff0c 在已排序序列中从后向前扫描 xff0c 找到相应位置并插入 形象的可以理解为打扑克抓拍的过程 xff0c
  • C/C++(3)C++调用C语言的函数和头文件

    C 43 43 语言支持函数重载 xff0c C语言不支持函数重载 函数被C 43 43 编译后在库中的名字与C语言的不同 xff0c C xff0b xff0b 和C是两种完全不同的编译链接处理方式 xff0c 如果直接在C xff0b
  • 一文了解IMU原理、误差模型、标定、惯性传感器选型以及IMU产品调研(含IMU、AHRS、VRU和INS区别)

    在此记录一下测试IMU过程中的其它文章 xff0c 便于以后查看 xff1a IMU的误差标定以及姿态解算ROS下通过USB端口读取摄像头数据 包括笔记本自带摄像头 激光 摄像头 IMU等传感器数据同步方法 message filters
  • windows安装Ubuntu子系统以及图形化界面记录

    文章目录 1 windows环境设置2 开始安装3 ubuntu使用3 1 启动和退出 Linux 子系统3 2 安装位置3 3 更换源 4 安装图形化界面4 1 安装VcXsrv4 2 安装桌面环境 xff08 1 xff09 方法1 x
  • STM32 DMA正常模式等待传输完成和开始下一次传输

    选择DMA的正常模式 xff0c 即DMA只传输一次 如果当传输完一次后 xff0c 还想再传输一次 xff0c 就需要重启DMA xff1a DMA Cmd DMA1 Channel6 DISABLE 重新设置源地址 重新设置目的地址 重
  • 增量式编码器和绝对式编码器,ABI信号和UVW信号、编码器PWM信号

    一 编码器的分类 根据检测原理 xff0c 编码器可分为光学式 磁式 感应式和电容式 xff0c 根据其刻度方法及信号输出形式 xff0c 可分为增量式 绝对式以及混合式三种 1 增量式编码器 增量式编码器是直接利用光电转换原理输出三组方波
  • 路由器接口管理 控制端口 辅助端口 物理端口 逻辑端口 局域网

    路由器接口管理 路由器的接口相对于交换机来说最大的特点就是接口类型和配置更为复杂 xff0c 一般吧路由器上的接口分为三大类 xff1a 1 局域网的LAN接口 xff0c 2 用于广域网接入 互联的WAN接口 xff0c 3 应用于LAN
  • C++各大有名库的介绍

    C 43 43 各大有名库的介绍 在C 43 43 中 xff0c 库的地位是非常高的 C 43 43 之父 Bjarne Stroustrup先生多次表示了设计库来扩充功能要好过设计更多的语法的言论 现实中 xff0c C 43 43 的
  • 树莓派搭建nas服务器的详细过程

    前奏 默认的登录帐号为 pi xff0c 密码是 raspberry 开启 ssh 在根目录 xff0c 新建一个名为 ssh 的空白文件就行了 然后 xff0c 重启就可以ssh访问了 命令行下配置 xff1a sudo raspi co
  • buffer overflow detected错误

    最近在写并行程序的时候遇到这个问题 在上网查询之后发现好多是由于sprintf的缓冲区不够造成的 对比自己程序发现一个很低级的错误 char sc 61 new char 100 sprintf sc 34 d 34 rank string
  • 聊一聊关于ROS开发常用的调试工具(rostopic,rosnode等等)

    ROS常用的调试工具有rosnode xff0c rostopic 1 rosnode 参数用法作用listrosnode list查看当前运行了哪些节点inforosnode info node name查看该节点发布 接受哪些话题以及服
  • 关于SLAM-Hector建图方法的一些基础点

    hector建图算法 1 简介 hector slam不需要里程计数据 xff0c 使用高斯牛顿方法 xff0c 直接使用激光雷达数据估算里程计信息 所以 xff0c 只需要激光雷达数据 xff0c 即可完成建图 2 优缺点 优点 xff1
  • 查全率(Recall)、查准率(Precision)以及综合评价指标(F1-Measure )

    xfeff xfeff 原文转载于 xff1a http www cnblogs com bluepoint2009 archive 2012 09 18 precision recall f measures html 在信息检索和自然语
  • 什么是码,主码,主属性,非主属性

    xfeff xfeff 码 xff1a 代表数目的符号 主码 我们在建立数据库的时候 xff0c 需要为每张表指定一个主码 xff0c 主码也叫主键 所谓主码就是在实体集中区分不同实体的候选码 一个实体集中只能有一个主码 xff0c 但可以
  • 整数二进制补码的数学原理(two's complement)

    转载自 整数二进制补码的数学原理 two 39 s complement 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61