我们都知道,补数 引申到 计算机中 就是 补码的概念, 可是 为什么呢? 我们先来 看看 各自的 定义……
补数:
在日常生活中,常常会遇到补数的概念。例如,时钟指示为6点,欲使它指示3点,既可按顺时针方向将分针转9圈,又可按逆时针方向将分针转3圈,结果是一致的。假设顺时针方向转为正,逆时针方向转为负,则有:
6 6
-3 +9
3 15
由于时钟的时针转一圈能指示12个小时,这“12”在时钟里是不被显示而自动丢失的,即15-12=3,故15点和3点均显示3点。这样-3和+9对时钟而言其作用是一致的。在数学上称12为模,写作 mod 12 ,而称 +9 是 -3 以 12 为模 的补数,记作:
-3 +9 (mod 12)
或者说,对模12而言, -3 和 +9 是互为 补数的。同理有:
-4 +8 (mod 12)
-5 +7 (mod 12) 为什么叫补数呢?我觉得是因为 | A | + | B | = M ,这样看起来 A 和 B 互补--和数学中两个角互补类似)
即对模 12 而言, +8 和 +7 分别是 -4 和 -5 的补数。可见,只要确定了“模”,就可找到一个与负数等价的正数(该正数即为负数的补数)来代替此负数,这样就可把减法运算用加法实现。例如:
设 A =9 , B =5 , 求 A - B (mod 12)
解,A - B = 9 - 5 = 4 (做减法)
对模 12 而言,-5 可以用其 补数 +7 代替, 即
-5 +7 (mod 12)
所以 A - B = 9 + 7 = 16 (做加法)
对模12而言,12会自动丢失,所以 16 等价于 4, 即 4 16 (mod 12) 。 # 联想 时钟, 就知道 16 和 4 指的位置是一样的,即作用相同。
进一步分析发现, 3点、15点、27点……在时钟上看见的都是3点, 即
3 15
27 (mod 12)
也即 3 3 + 12
3 + 24
3 (mod 12)
这说明,正数相对于“模”的补数就是正数本身。(例如在时钟上,15,27等始终指的都是3点,模12会自动丢失;一个数的补数等于这个数+模,再对模取余;所以补数 一定不大于模;而负数,在时钟上,表示逆时针旋转将分针旋转 | A | 圈,但由于时钟本身是顺时针旋转的,且 顺时针 旋转 M - | A | 圈,也能指向和逆时针旋转 | A | 圈一样的位置,所以 负数 可以 用其补数 来代替,这样就都是 顺时针旋转,即 运算中 就是 加法)
# 在时钟中,补数 只有正数的概念,即便 是 负数,也应当转为 正数的补数形式, 不然 不具有 实际意义。
上述,补数的概念 可以用到 任意 的 “模” 上,如:
-3 +7 (mod 10)
+7 +7 ( mod 10)
-3 +97 ( mod 10^2)
+97 +97 ( mod 10^2)
-1011 +0101 ( mod 2^4)
+0101 +0101 ( mod 2^4)
-0.1001 +1.0111 ( mod 2)
+0.1001 +0.1001 ( mod 2)
由此可见:
1、一个负数可以用它的正补数来代替,而这个正补数可以用模加上负数本身求得;
2、一个正数和一个负数互为补数时,它们绝对值之和即为模数;
3、正数的补数还是该正数本身。
将补数的概念,引申到计算机中,就出现了补码这种机器码。 我们来看看补码的定义: (以下,均来自唐朔飞的计组原理)
#可以看到,在上述计算机定义里,补数的模长定为 2^(n+1) ,但是在计算机一个字节为8位,为表示有符号数(拿出一位表示符号),那么n=7就够了。(这里不考虑双符号位的补码)
同时: M = 2^8 = 1 0000 0000 = 1111 1111 + 0000 0001 ; 对于 求 -1的补数(补码),也就变为 1111 1111 - 0000 0001 + 0000 0001 = 1111 1111
所以,求补码的公式也就出来了,全部取反后 +1;但考虑到 符号位的概念, 上式 变为 111 1111 - 000 0001 = 111 1111 ,加上符号为 1111 1111;公式就成了 负数的补码 是 “除 符号位以外,其余各位按位取反后 ,加1” .
但是,最前面的 1 就是 负数 的补数(补码)的第一位啊,为什么,我们可以 规定 它的第一位为 1 代表负数。 因为,我们规定了一个8位二进制的 正数的符号位(第一位)为0 ,这样,不管 正数 是多少,其对应的负数, 最开始的 第一位 一定 是 1 , 你信,你可以 自己 举任何例子。 这样,不管是 有符号位,还是没有符号位, 其 求出 的 负数的补数 (补码) 都是正确的。 况且,一个数本来是负数,你用补数代替了,岂不成正数了,为了让补码(补数)在计算机中能被还原 为 负数,得让计算机 识别 这个补数 是 负数啊;计算机 就是 根据 首位 是 1 还是 0 ,来看 这个 补数(补码)是否为 负数的 ,然后 再 还原补数为 真正的 负数 和 正数 , 所以 符号位 必须得有。
我们再来看看 计算机中 ,如何用 补码 来运算?
1、用补码来表示负数就可以将加减法统一成加法来运算,简化了运算的复杂程度。 减法变为 被减数 + 减数相反数的补码。
例如: 8 - (-8) == 8 + 8(-8补码的相反数的补码);
0000 1000 + 0000 1000 (-8的补码1111 1000的相反数补码是 0000 1000 ) = 0001 0000 这也就是 10进制 的 16
我们一般求一个数相反数的 补码思路是:补码-->原码 -->相反数原码 -->相反数补码
但实际上,可以 直接 套 公式 : (11111111 - 一个数的补码)+00000001=它相反数的补码 ; 假设 这个数 是 -8;代入公式有:
(11111111 - 1111 1000)+00000001 = 0000 0111 + 0000 0001 = 0000 1000 即它相反数(8)的补码。
#这公式 也说明了,为什么,一个负数的补码是 其反码+1 , 1111 1111 - 一个数的补码 就是 按位取反,+ 0000 0001 就是加1。
# 上式 总结为: "一个数 补码的补码 就是 其相反数的 补码" (无论这个数是 正数 还是 负数)
# 再有就是, -8 补码是 1111 1000 再求 补 后 为 8的 补码, 我们惊奇的 发现,8的补码就是 8的原码,所以正数 原 ,反,补一致。
# 当然,这里,我这里 ,可以说是 强行解释, 但也 不无 道理。
2、采用补码进行运算有两个好处,一个就是刚才所说的统一加减法;二个就是可以让符号位作为数值直接参加运算,而最后仍然可以得到正确的结果符号,符号位无需再单独处理。
例如: 8 - 3 = 5 这个式子, 我们用 补码 来运算 一下
0000 1000 + (-3)补 = 0000 1000 + 1111 1101 = 0000 0101 而这个 同样是 补码表示法, 但符号为0(正数原反补一致),所以一看 就是 5 ; 所以 式子 是正确的。
再看 8 - 9 = -1 这个式子,被减数 和 减数 分别转为 补码后如下:
0000 1000 + (-9)补 = 0000 1000 + 1111 0111 = 1111 1111 ,而 这个 结果 不就是 -1 的 补码,同样正确,所以 确实如上述 补码优点 一样。
3、再来看一个有趣的现象:
8 的 补码是 0000 1000 ; 其相反数的补码是 1111 1000 ;而这个是怎么来的呢,就是0000 1000 全部取反为 1111 0111 然后 +1 ,得到 1111 1000;
而这 与 上述 1 是一致的。那么,给定一个 负数,我们求它的补码 , 就有两种方法:第一种是 该负数相反数(即正数)原码全部取反后+1;第二种是负数原码 除符号位 以外,全部取反后+1 。
4、如果你还是对 补数为什么能引进计算机中,成为 补码 不明白,我再讲下自己的理解。
补数 最大的特点是 补数之间做运算时,减法同样可改为 加法,考虑到 模的周期性和不显现性,使得 减法修改后的加法 运算 得出的 结果 也是正确的。
就如 最开始所说的, 9 -5 (mod 12) 这个式子, 改为加法后是 9 + (12 - 5) = 9 + 7 = 16 , 由于 模12的不显现性 ,所以 16最终 - 12 得到 4 ,同样得到正确的结果;再来 8 - 9 (mod 2^n)这个式子,改为加法后 8 + 2^n - 9 = 2^n -1 ,计算机中n=8,2^8-1 就是 1111 1111,这就是-1的补码啊,从中,可以发现,用补码进行运算是完全正确的,没有问题的。计算机中的补码同样是 补数(只不过,计算机中模 为 2^(n+1)次方罢了,且均用二进制表示而已),所以 必然 也满足这个性质/规律, 所以 计算机中 补码 代替 原码 进行运算 也是正确的。 那么,你可能又要问了,要是两个数的和 大于 模 ,怎么办? 在 模的周期性和不显性下,进位岂不是 丢失掉了 。 这个,不需要我们考虑, 因为 进位 完全 可以 存放在 高位 字节 里, 丝毫 不影响 补码 的运算。
好了,不能再想这个问题了,不然 我又要花很多时间了,以上 纯属 个人观点,欢迎大家参考指正。