一 :环模型
1. 模型引入
对于计算机而言,计算机里面位数通常是固定的,画出来的图形是类似一个环,如下所示。
以3位二进制为例,从000 到 111。
(这是无位数限定的图)
(这是固定位数限定的图)
(原本应是111+1=1000,但因位数限定,导致其中最高位1被舍去,变为111+1=000)
参考:
http://igoro.com/archive/why-computers-represent-signed-integers-using-twos-complement/
2. 模型介绍
先介绍一个概念:模算数(modular arithmetic)
维基百科:
模算数(modular arithmetic)是一个整数的算术系统,其中数字超过一定值后(称为模)后会“卷回”到较小的数值,模算数最早是出现在卡尔·弗里德里希·高斯在1801年出版的(《算术研究》《Disquisitiones Arithmeticae》)一书中。
(具体概念,可自行百度或Google, 模,同余,模算数等作为关键词)
补码本质上就是模算数,是比计算机还要早的出现。
对于模算术,可用环图作为模型,可以想象成一个有限的环集合,也可以解释为一个可溢出计数系统,其中计数系统容量作为模。(也有点类似周期)
可以看一个钟表问题来加深理解。
(时钟算术)
问题是:下图的时钟如何从6点拨到3点
方法一:逆时针拨3个小时
方法二:顺时针拨9个小时
其实不止两种方法,你多绕几圈也可以,方法二的算术为,6+9=15-12=3。因为15超出了模的范围,所以要减去模12。
参考:
https://zhuanlan.zhihu.com/p/91964219
3. 模型特点
(1)数轴运算
先说说我们平时使用的四则运算,如果用模型来解释的话,可以画一条数轴。
所谓的加减乘除(四则运算)都是方向上的叠加(即向量叠加),如下图所示。
(画的不太准,见谅)
(2)环运算
1.方向
假如设顺时针为正方向,逆时针为负方向,如下图。
2.运算
(1) 正方向叠加(模为8为例)
情况一:叠加未超出模范围
情况二:叠加超出模范围
情况三:一个正方向数加上模(情况三为情况二的特殊形式)
(2) 负方向方向叠加(只给出情况三,情况一二和上面类似)
(3) 正负方向叠加
(3)相等性(术语:同余)
上述的四个图在环中所表示点位置都相等,如果上述用数轴表示的话,即 -11(5-8x2) ≡ -3(5-8) ≡ +5≡ 13(5+8) (mod 8)读作a同余于b模m,
对于模(也就是在这个环上)同余是一种等价关系。
总结一下,就是 a-b的差值为模n的整数倍数(倍数包括正负倍数和0)。那么a和b同余,即在环上相等。
(4)环与数轴的比较
环和数轴本来就是两种模型,数轴表示无限数集,环表示有限数集。
环上有一个特点,即负数可以等于正数,这是数轴所没有的特性。
这也是计算机采用补码表示的原因。
二 :计算机编码
这里的编码本质上就是数轴(真值)和环(机器数)的一种映射。比如【0,255】,【-128,127】等和【0000 0000,1111 1111】相互一一对应(8位二进制数为例)。因为去使用数轴去编码环,自然会出现溢出现象。
(1)无符号编码(8位二进制为例)
其实就是二进制的【0000 0000 ,1111 1111】和【0,255】的一一映射。
这里的 环上的 1111 1111 + 1 = 0000 0000 对应的数轴为 255 + 1 = 0 ,所以产生了溢出。
(2)有符号编码(8位二进制为例)
对于有符号的编码,大致有四种:1 . 原码 2 . 反码 3 . 补码 4 . 移码。
不过我们先不看这四种编码,我们单纯从环模型上看,如何把数轴【0,255】和环【0000 0000 ,1111 1111】的映射
改为数轴【-128,+127】或【-127,+128】和【0000 0000 ,1111 1111】的映射。(因为有0的缘故,所以正的的负的数量不相等,为什么会有这个【-127,+128】的存在,后文会说)
看图
如图所示,如果计算机系统中加法器和减法器都存在的话,有符号编码应该是上面所示,那么关于1开头的机器数怎么编码,可以利用环的
同余性,也就是相等,也就是上面的(3)相等性(术语:同余)的例子那样,即下图
然后我们惊奇的发现,负数都能用正数来表示了,也就是【-128,-1】对应【1000 0000 , 1111 1111】,既然如此,要减法器何用,所以直接把上图的右侧撤除掉,即计算机里,有符号编码也只用加法器,另一个更重要的原因是,从硬件角度来说,减法器比加法器可贵太多了,复杂太多了。
于是乎,只剩下下面这个。数轴【-128,+127】和【0000 0000 ,1111 1111】的映射图,也就是我们所说的补码
这里提及一下,因为在环上,1000 0000 的编码你取 +128 还是-128 都是ok的,但是人为规定以0开头为正数,1开头为负数,即第一位为符号位,因为1000 0000是1开头的,所以变成了-128(也就是传说中的人为规定。。。)
这就是补码的编码方式,其实在环图上,这种编码是很自然的方式,并不涉及符号之类的东西,以及原码和反码。
然后呢,原码因为对于负数的编码与补码相反,所以无法进行有关负数和负数,以及负数和正数之间的四则运算,但是原码具有易于真值转
换的优点,所以可以通过真值→原码→反码→补码的方式来求补码。
反码:反码编码与补码相似,如下图
但是有正负零的问题,导致如果要用反码来进行四则运算,会出现“循环进位”的问题,即如果产生进位,就要多加个1。
移码,上图:
由上图可以很容易的得知,移码就是把数轴的编码变成类似无符号数那样,因为移码是用于浮点数的阶码,这样编码易于机器数直接的比较大小。
三 :其他
无符号的二进制和真值之间的转换如下:
例:(无符号的1111 1111 = 255)
1111 1111 = 27 + 26 + 25 + 24 +23 + 22 + 21 + 20 =128 + 64 + 32 + 16 + 8 + 4 +2 + 1 = 255
那有符号补码的求法除了原码的取反+1,其实还能把上面的“按权展开法”的最高位变为负权就行了,
例:(有符号的补码1111 1111 = -1)
1111 1111 =(- 27) + 26 + 25 + 24 +23 + 22 + 21 + 20 =(-128) + 64 + 32 + 16 + 8 + 4 +2 + 1 = -128 +127 = -1
为什么会这样呢?很简单
如上图所示,把无符号数减去模256(256 = 28)就变成了补码形式:
255-256 = -1 对应的机器数为
1111 1111(补码) = 27 + 26 + 25 + 24 +23 + 22 + 21 + 20 -256=(27 - 28)+ 26 + 25 + 24 +23 + 22 + 21 + 20 =
( -27) + 26 + 25 + 24 +23 + 22 + 21 + 20 = -128 + 127 = -1