forked from dreamsxin/example
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
88 additions
and
0 deletions.
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
# 补码与模的概念 | ||
|
||
## 模的概念 | ||
|
||
把一个计量单位称之为模或模数。例如,时钟是以12进制进行计数循环的,即以12为模。 | ||
|
||
在时钟上,时针加上(正拨)12的整数位或减去(反拨)12的整数位,时针的位置不变。14点钟在舍去模12后,成为(下午)2点钟(14=14-12=2)。从0点出发逆时针拨10格即减去10小时,也可看成从0点出发顺时针拨2格(加上2小时),即2点(0-10=-10=-10+12=2)。因此,在模12的前提下,-10可映射为+2。 | ||
由此可见,对于一个模数为12的循环系统来说,加2和减10的效果是一样的;因此,在以12为模的系统中,凡是减10的运算都可以用加2来代替,这就把减法问题转化成加法问题了(注:计算机的硬件结构中只有加法器,所以大部分的运算都必须最终转换为加法)。 | ||
10和2对模12而言互为补数。同理,计算机的运算部件与寄存器都有一定字长的限制(假设字长为16),因此它的运算也是一种模运算。 | ||
当计数器计满16位也就是65536个数后会产生溢出,又从头开始计数。产生溢出的量就是计数器的模,显然,16位二进制数,它的模数为2^16=65536。 | ||
|
||
## 两个互补的数称为“补码” | ||
|
||
比如一个有符号8位的数可以表示256个数据,最大数是01111111(+127),最小数10000000(-128);那么第255个数据,加2和减254都是一样的效果得出的结果是第一个数据,所以2和254是一样的效果。对于255来说2和254是互补的数。 | ||
|
||
求一个正数对应补码是一种数值的转换方法,要分二步完成: | ||
第一步,每一个二进制位都取相反值,即取得反码;0变成1,1变成0。比如,00001000的反码就是11110111。 | ||
第二步,将上一步得到的反码加1。11110111就变成11111000。所以,00001000的二进制补码就是11111000。也就是说,-8在计算机(8位机)中就是用11111000表示。 | ||
不知道你怎么看,反正我觉得很奇怪,为什么要采用这么麻烦的方式表示负数,更直觉的方式难道不好吗? | ||
|
||
二进制的补码就是最方便的方式。它的便利体现在,所有的加法运算可以使用同一种电路完成。 | ||
还是以-8作为例子。假定有两种表示方法。一种是直觉表示法,即10001000;另一种是2的补码表示法,即11111000。请问哪一种表示法在加法运算中更方便?随便写一个计算式,16 + (-8) = ?16的二进制表示是 00010000,所以用直觉表示法,加法就要写成: | ||
00010000 | ||
+10001000原码形式-8 | ||
--------- | ||
10011000 | ||
可以看到,如果按照正常的加法规则,就会得到10011000的结果,转成十进制就是-24。显然,这是错误的答案。也就是说,在这种情况下,正常的加法规则不适用于正数与负数的加法,因此必须制定两套运算规则,一套用于正数加正数,还有一套用于正数加负数。从电路上说,就是必须为加法运算做两种电路。所以用原码表示负数是不行的。 | ||
现在,再来看二进制的补码表示法。 | ||
00010000 | ||
+11111000补码形式-8 | ||
--------- | ||
100001000 | ||
可以看到,按照正常的加法规则,得到的结果是100001000。注意,这是一个9位的二进制数。我们已经假定这是一台8位机,因此最高的第9位是一个溢出位,会被自动舍去。所以,结果就变成了00001000,转成十进制正好是8,也就是16 + (-8) 的正确答案。这说明了,2的补码表示法可以将加法运算规则,扩展到整个整数集,从而用一套电路就可以实现全部整数的加法。 | ||
二进制补码的本质,本质是用来表示负整数的 | ||
|
||
在回答二进制补码为什么能正确实现加法运算之前,我们先看看它的本质,也就是那两个求补码步骤的转换方法是怎么来的。下面描述了一个正数怎么求它对应负数在计算机的表达方式。比如128,正数为10000000,但是惊奇的发现-128也是10000000。但是这里由于属于数据类型的限定,第八位同样一个1代表不同的含义,前面的 1是数值位,后面数的 1是符号位。 | ||
要将正数转成对应的负数,其实只要用0减去这个数就可以了。比如,-8其实就是0-8。用模数的概念解释如下图 | ||
|
||
 | ||
|
||
已知8的二进制是00001000,-8就可以用下面的式子求出: | ||
00000000 | ||
-00001000 | ||
---------- - - - | ||
因为00000000(被减数)小于0000100(减数),所以不够减。请回忆一下小学算术,如果被减数的某一位小于减数,我们怎么办?很简单,问上一位借1就可以了。 | ||
所以,0000000也问上一位借了1,也就是说,被减数其实是100000000,这是重点;算式也就改写成: | ||
100000000 | ||
-00001000 | ||
---------- - - | ||
11111000 | ||
进一步观察,可以发现可分拆为100000000 = 11111111 + 1,所以上面的式子可以拆成两个: | ||
11111111 | ||
-00001000 | ||
--------- | ||
11110111取反 | ||
+00000001加一 | ||
--------- | ||
11111000 | ||
二进制的补码两个转换步骤就是这么来的。 | ||
举个例子,比如-128补码的由来,先把正整数128二进制表示出来10000000求-128的补码 | ||
1 1 1 1 1 1 1 1 | ||
-1 0 0 0 0 0 0 0 | ||
--------- | ||
0 1 1 1 1 1 1 1 | ||
+0 0 0 0 0 0 0 1 | ||
--------- | ||
1 0 0 0 0 0 0 0 | ||
即-128的补码是10000000。8位的结构能表示的最小数是-128; | ||
所以可以总结求补码的范式是这样的: | ||
求n位系统的一个数正数A : 01101101101……….11101100(n位二进制),怎么求他的补码呢,就用n位的1111111111111111111…..111(n位) - 11101101101……….11101100(n位二进制) + 1 = A的补码就行啦!但是 | ||
如果一个1111111111111…..111111(n位全为1的正整数的补码),要用1111111111111…….11111(n+1位) - 1111111111111…..111111(n位全为1的正整数) +1 才能求的她对应的补码。 | ||
如uint16 A =200, uint16 B =65535,那么C =A-B; | ||
65535的补码:正数65535为1111 1111 1111 1111,进行下面的计算求得B的补码即-B;先展示有补码符号位,即补码有最高位位1的; | ||
1 1111 1111 1111 1111 -1111 1111 1111 1111 +1 =1 0000 0000 0000 0001,相当于被减数是10 0000 0000 0000 0000(18位) =1 1111 1111 1111 1111 +1 | ||
因为A和B 都是16位的无符号数,所以65535的补码最高位舍去,相当于被减数是1 0000 0000 0000 0000 =1111 1111 1111 1111 +1,即可以用上面的范式方法,但是这样-B就没有体现它的负数的符号位了;当然这是因为16位运算超出16位的位都舍去了。即-B=1;即A-B= 200+1 =201。其实也可以用模数概念解释A -B;如下图正数的模数 | ||
|
||
 | ||
|
||
## 为什么正数加法也适用于二进制的补码? | ||
|
||
实际上,我们要证明的是,X-Y或X+(-Y)可以用X加上Y的2的补码(-Y)完成。 | ||
Y的二进制补码等于(11111111-Y)+1。所以,X加上Y的2的补码,就等于:X + (11111111-Y) + 1;我们假定这个算式的结果等于Z,即 Z = X + (11111111-Y) + 1。 | ||
接下来,分成两种情况讨论。 | ||
第一种情况,如果X小于Y,那么Z是一个负数。这时,我们就对Z采用补码的逆运算,就是在做一次求补码运算,求出它对应的正数绝对值,只要前面加上负号就行了。所以, | ||
Z = -[11111111-Z+1] = -[11111111-(X + (11111111-Y) + 1)+1)] = X - Y;这里如果X Y Z都是无符号型的,且X < Y 那么Z 最终得到的数是|X-Y|距离的绝对值了,比如X=1,Y= 255,那么Z=2,因为从255到1只要加两次就到了。这里你不要问我为什么,这里就用到上面的模概念。 | ||
第二种情况,如果X大于Y,这意味着Z肯定大于11111111,但是我们规定了这是8位机,最高的第9位是溢出位,必须被舍去,舍去相当于减去吗!所以减去100000000。所以, | ||
Z = Z - 100000000 = X + (11111111-Y) + 1 - 100000000 = X - Y | ||
这就证明了,在正常的加法规则下,可以利用2的补码得到正数与负数相加的正确结果。换言之,计算机只要部署加法电路和补码电路,就可以完成所有整数的加法。 |