原码和补码,真的很难学
因为我上课没好好听
什么是机器数
机器数,是与真值相对的概念,指的是被计算机内部编码后表示的数。而真值指机器数真正的值,对应现实世界中带有正负号的数。
例如,负数在计算机中是以补码的形式存储的,-10用8位补码表示为11110110,说明机器数11110110B的真值是-10。
机器数一定是一个0/1序列,通常缩写成十六进制形式。
这里贴一个小工具,原码/反码/补码计算器
http://www.atoolbox.net/Tool.php?Id=952
原码表示法
一个数的原码表示由符号位Sign和数值位Magnitude构成,因此原码表示法也成为“符号-数值表示法”。
在原码表示法中,正数和负数的编码只有符号位上的不同。
原码编码规则如下:
-
当X为n位正数时,X n-1 = 0,X i = X i ’
-
当X为n位负数时,X n-1 = 1,X i = X i ’ ( 0 ≤ i ≤ n-2)
这个X的上角标有一撇,但是特别小,无语,你电脑屏幕要是没擦干净绝对找不着
原码中0有两种表示形式:
3. [ +0 ] 原 = 000…0
4. [ -0 ] 原 = 100…0
原码与真值的对应关系直观、方便,与真值之间的转化简单,并且用原码实现乘除运算也比较简便。但是其0的表示不唯一,而且原码的加减运算规则复杂(见下文补码部分分析)。
现代计算机中不用原码来表示整数,只用定点原码小数来表示浮点数的尾数部分。
浮点数笔记:https://blog.csdn.net/Tea_Char/article/details/112297035
补码表示法
在计算机中,补码用来表示带符号整数。补码表示法也称“two’s complement”表示法,由符号位后跟上真值的模2n补码构成。
模运算
在模运算系统中,若A、B、M满足 A = B + k ⋅ M A=B+k · M A=B+k⋅M其中k∈Z,则记为 A ≡ B ( m o d M ) A \equiv B(mod M) A≡B(modM)即A、B各除以M后的余数相同,故称B和A模M同余。
钟表就是一个典型的模运算系统,其模数为12.
假定在钟表上只能顺时针方向拨动时针,如何用顺拨的方式实现将10点倒拨4格?拨动后钟表上是几点?
10 − 4 ≡ 10 + ( 12 − 4 ) ≡ 10 + 8 ≡ 6 ( m o d 12 ) 10-4 \equiv 10+(12-4) \equiv 10+8 \equiv 6(mod 12) 10−4≡10+(12−4)≡10+8≡6(mod12)
因此可从10点顺时针拨8格来实现倒拨4格,最后是6点.
在上述题目中,8就是-4的补码。
在计算机内部,存储、运算和传送部件都是有有限位的,因此计算机中机器数的位数也是有限的,两个n位数在运算过程中可能生成多于n位的结果,此时高位就会被舍弃。之后会有两种可能的结果:
- 剩下的低n位数不能表示正确的运算结果,即结果溢出 overflow
舍弃高位的操作相当于将一个多于n位的数除以2^n,保留其余数作为结果,也就是模运算操作。
- 剩下的低n位数能够表示正确的运算结果。
补码的定义
了解了上述原码和模运算的概念,我们可以引出补码的表示:正数的补码符号Sign为0,数值部分就是其本身;负数的补码等于模与该负数绝对值之差。
- 当X为正数时, [ X ] 补 = X = M + X ( m o d M ) [X]_{补}=X=M+X(mod M) [X]补=X=M+X(modM)
- 当X为负数时,
[
X
]
补
=
M
−
X
(
m
o
d
M
)
=
M
+
X
(
m
o
d
M
)
[X]_{补}=M-X(mod M)=M+X(mod M)
[X]补=M−X(modM)=M+X(modM)
综合上诉两点,可以得到结论:
对于任意一个数X, [ X ] 补 = M + X ( m o d M ) [X]_{补}=M+X(mod M) [X]补=M+X(modM)
对于具有一位符号位,n-1位数值位的n位二进制整数X的补码来说,其补码的定义如下:
[
X
]
补
=
2
n
+
X
(
−
2
n
−
1
≤
X
<
2
n
−
1
,
m
o
d
2
n
)
[X]_{补}=2^n+X(-2^{n-1} \leq X < 2^{n-1}, mod2^n)
[X]补=2n+X(−2n−1≤X<2n−1,mod2n)
特殊数据的补码表示
-
补码位数为n时(其模为2n),求-2(n-1)的补码表示
[ -2n-1 ]补 = 2n-2n-1 = 2n-1(mod 2n) = 10…0 (n-1个0) -
补码位数为n+1位时(其模为2(n+1)),求-2(n-1)的补码表示
[ -2n-1 ]补 = 2n+1-2n-1 = 2n+2n+1(mod 2n+1) = 110…0 (n-1个0) -
设补码的位数为n,求-1的补码表示
[ -1 ]补 = 2n-1 = 11…1 (n个1) -
0的补码表示
[ +0 ]补 = [ -0 ]补 = 2n ± 0 = 100…0(mod 2n) = 00…0(n个0)
从上述结果可以得知0的结果是唯一的,这带来了一些好处:
- 减少了+0与-0之间的转换问题
- 少占用一个编码表示,使补码比原码能多表示一个最小负数。比如我们用00…0表示0,那原本表示-0的补码100…0就用来表示最小负整数-2(n-1)了,这也是为什么整型int的范围是(-231,231-1)而不是(-231+1,231-1)
补码与原码之间的转换
已知补码的位数为8,求1101100和-1101100(真值)的补码表示
补码总位数为8,就有7位数值位和1位符号位.
[ 1101100 ]补 = 28+110 1100 = 1 0000 0000 +110 1100(mod 28) = 0110 1100
[ -1101100 ]补 = 28-110 1100 = 1 0000 0000-110 1100 = 1001 0100(冗长繁琐的计算过程,算不出也没事,下面有简单方法)
仿照上述的计算过程得到结论,负数的补码计算步骤为:
符号位取1,其余各数值位取反加1
因此,可以用过简单的方法来求一个数的补码,
已知[ X ] 原 求[ X ] 补 :
- 对于正数,符号位取0,其余各位同原码数值位中对应各位
- 对于负数,符号位取1,其余各位由数值位“各位取反,末位加1”得到
已知补码位数为8,用简便方法求X=-110 0011的补码表示
[ X ]原 = 1 110 0011
[ X ]补 = 1 001 1100 + 0 000 0001 = 1 001 1101
已知[ X ] 补 求[ X ] 原 :
- 对于符号位为0的数,原码同其补码.
- 对于符号位为1的数,原码符号位取1,数值位为其补码各数值位取反再加1.
已知补码位数为8,[ X ] 补 =1 001 0100,求真值X
X=-(100 1011 +1 )=-100 1100
已知[ X ] 补 求[ -X ] 补 :
- 对于符号位为0的数,将[ X ] 补 的符号位取1,此时得到的是[ -X ] 原 ,再将其各数值位取反再加1,就得到[ -X ] 补 。
- 对于符号位为1的数,对其补码表示再求补码,此时得到[ X ] 原 ,再将[ X ] 原 符号位取1,此时得到[ -X ] 原 ,再对其各数值位取反再加1,得到[ -X ] 补 。
总结可以得知,由[ X ] 补 求[ -X ] 补 的方法是:各位(包括符号位)取反,末位加1
已知[ X ] 补 = 1 011 0100,求[ -X ] 补
[ -X ] 补=0 100 1011 + 0 000 0001 = 0 100 1100
注意最小负数取反后结果会溢出
已知[ X ] 补 =1 000 0000,求[ -X ] 补
[ -X ] 补=0 111 1111 + 0 000 0001 = 1 000 0000(结果溢出)
8位整数补码1000 0000对应的是最小负数-27,对其取反后的值为27(即128),8位整数补码能表示的最大正数为27-1=127,而数128无法用8位补码表示,结果溢出。
反码表示法
负数的补码可通过符号位不变,其余各位取反,末位加1的方法得到,如果仅仅是各数值位取反而不加1,就可以得到负数的反码表示,因此负数反码的定义就是在相应的补码表示中再末位减1。
正数的反码是其本身
反码表示存在以下几点不足:
- 0的表示不唯一
- 表数范围比补码少一个最小负数
- 运算时必须考虑循环进位
因此反码在计算机中很少被使用,有时用作数码变换的中间表现形式或用于数据校验。