由于数据的机器级表示相对抽象,在重温这块内容时,写一下思路,以便重新理解一遍.但是本文并不严谨,一切的目的只是为了方便直观上理解,也为了服务于考试,而不是搞懂它背后的数学逻辑.如有错误,还请批评指正.
补码是什么?
首先我们要先明确,补码只是针对有符号数而言的,对于无符号数,我们不谈补码.(实际上对于有符号的正整数而言,原反补都是一样的).
在转补码时,其实并不费劲,就是负数补码就是取反加一,这也是补码的核心原则.但个人认为,补码的关键在于符号位可以直接参与运算.在计算机内部,我们实现两数相减也就是基于此的.
这里我们以一个简单的例子来看一下, 假设现在有数 A = ( 01 ) 2 , B = ( 10 ) 2 A=(01)_2,B=(10)_2 A=(01)2,B=(10)2,均为补码表示,那么 A + B = ( 11 ) 2 A+B=(11)_2 A+B=(11)2. 实际上最后的结果为 − 1 -1 −1.那么我们这里已知 A = 1 , B = − 2 A=1,B=-2 A=1,B=−2的情况下,似乎**符号位参与运算的直接结果,就是将符号位直接视为 − 2 n − 1 -2^{n-1} −2n−1**来参与运算的.
我们再举一个更加一般的例子: A = 010010 ( 18 ) , B = 100111 ( − 32 + 7 ) A=010010(18),B=100111(-32+7) A=010010(18),B=100111(−32+7).这个例子显然 A A A为正数, B B B为负数,那么它们做加法的最后结果 A + B = 111001 ( − 32 + 18 + 7 ) A+B=111001(-32+18+7) A+B=111001(−32+18+7).将 B B B分解为 100000 + 000111 ( − 32 + 7 ) 100000+000111(-32+7) 100000+000111(−32+7),上面所说的结论就有点显然了.
所以我们可以看到一点端倪,所谓符号位可以参与运算,就是把符号位直接视为 − 2 n − 1 -2^{n-1} −2n−1,其他位置按照正数直接计算,就是补码加减的结果.
由此我们也就解释了,为什么补码从 011..111 011..111 011..111到 100...000 100...000 100...000,会直接从最大数跳变到最小数,也同样解释了,为什么在补码下, 0 0 0是唯一的.(当然上述的结论并没有严格的数学证明,但实际上可以证明它们是成立的,具体可参见
CSAPP
相关章节).
怎么样不转原码速算补码?
上述结论给我们提供了一个相当有效的计算方式.
- 对于一个正数而言,0xxxxx,就是它的原码,直接算就可以了.
- 对于一个负数而言,1xxxxx,就直接把符号位视为 − 2 n − 1 -2^{n-1} −2n−1,后面的数字部分当成一个正数,然后算两者相加即可,比如 100101 → 100000 ( − 32 ) + 000101 ( 5 ) = − 27 100101\rightarrow100000(-32)+000101(5)=-27 100101→100000(−32)+000101(5)=−27.
对于第二种方法的介绍,我们要结合一点符号扩展的内容,首先明确,对于一个 n n n位有符号数,扩展为一个 2 n 2n 2n位的有符号数,我们要把高 n n n位全部置为这个数的符号位.对于 1010 1010 1010扩展为八位就是 11111010 11111010 11111010. 0101 0101 0101的扩展就是 00000101 00000101 00000101.从而不改变这个数字的大小.
如此我们考虑 000011 000011 000011其实和 011 011 011是完全一样的, 这里保留一位 0 0 0的目的是看清楚它的符号位.这其实就相当于把一个扩展过的数恢复为原来的数一样.那么考虑 11110101 11110101 11110101,实际也就是等同于 10101 10101 10101.
如此我们便得到了第二种速算补码的方式,即对于形如 11..110... 11..110... 11..110...这样的补码,它的高 m m m位全为 1 1 1,我们可以直接把高 m m m位的 1 1 1省略,保留低 m − n m-n m−n位,然后用第一种方法算就可以了.
这里要说明的是,上述的两种计算方法都没有做严格的数学证明,但是从直觉上来看是显然的,实际上从数学上也可以严格证明,这里我们从略.
移码是什么?
所谓移码,就是将数轴上的一段一一映射到另一段.
比如 1 − 10 1-10 1−10,有一个映射 f ( x ) : x + 10 f(x):x+10 f(x):x+10,那么 1 − 10 1-10 1−10就会被映射到 11 − 20 11-20 11−20.移码只不过是将上述过程放到二进制的视角下看待.
译码的偏移量为 2 m 2^m 2m.即对于 − 2 m ∼ 2 m − 1 -2^m\sim2^m-1 −2m∼2m−1,这段数据,就可以被映射到 − 2 m + 2 m ∼ 2 m − 1 + 2 m -2^m+2^m\sim2^m-1+2^m −2m+2m∼2m−1+2m,即 0 ∼ 2 m + 1 − 1 0\sim2^{m+1}-1 0∼2m+1−1.
各个码的取值范围是多少?
以 n n n位二进制数为例,对于 n n n位二进制数,最多可以表示 2 n 2^n 2n个数字.
- 原码
原码中 0 0 0的表示范围有两种,故原码最多可以表示的数字的数量为 2 n − 1 2^n-1 2n−1.最大 011..1 011..1 011..1,最小 111..11 111..11 111..11.故原码的范围 − 2 n − 1 + 1 ∼ 2 n − 1 − 1 -2^{n-1}+1\sim2^{n-1}-1 −2n−1+1∼2n−1−1.- 反码
反码和原码的区别就是按位取反,其他完全一样,那么显然,表示范围也完全一样.- 补码
补码中 0 0 0的表示唯一,根据前面的分析不难看出,补码最小值为 100..00 100..00 100..00,最大值为 011..11 011..11 011..11.故补码的范围 − 2 n − 1 ∼ 2 n − 1 − 1 -2^{n-1}\sim2^{n-1}-1 −2n−1∼2n−1−1.- 移码
事实上不难看出,移码是将原来的原码平移了 2 m 2^m 2m,那么对于移码而言,就没有符号位的说法了(当然,如果数据本身是 2 m 2m 2m位的话,这里的第一位仍然可以视为一种形式符号位).基于此,我们可以知道,移码中也不存在 0 0 0占两个位置的情况,所以它的范围和补码是一样的,即 − 2 n − 1 ∼ 2 n − 1 − 1 -2^{n-1}\sim2^{n-1}-1 −2n−1∼2n−1−1.(这里指的是没有移位的原始数).