原码、反码、补码的理解

本文主要论述了计算机中原码、补码、反码等相关知识点、及其应用与计算。仅个人理解,如有错误,望诸位指正于评论区。

KR

先在这里提前写下结论,以方便于以后查阅,尚未了解原码、补码等可跳过这部分。

  1. 真值:数值的大小,包括符号

  2. 机器数(二进制数形式,包括原码、反码、补码、):

    • 原码:正数符号位0,负数符号位1,数值按权展开。8位范围:-127D ~ 127D
    • 反码:正数的反码与原码一致,负数的反码是,仅将原码的数值位置取反。8位范围:-127D ~ 127D
    • 补码:正数与原码相同,负数的补码是,反码加1。8位范围:-128D ~ 127D
  3. 补码计算:

    • 二进制:正不变,负取反加一。
    • 十进制:正不变,负数总范围量减去数值,即2^n - xxx
    • 十六进制:十进制转十六进制即可。(负数也可以用全F减,再加一

前储认知

首先,我们需要了解一件事:计算机是电子设备,只认识0与1,不认识我们人类所用的汉语、英文、涂鸦等。

可以设想,电的信号有两个状态,你可以理解成路的“开”与“关”,电位的“高”与“低”等,我们就可以用0与1表示这两个状态。所以电子设备就有了属于自己的“语言”描述它们的认知。

但是只有0与1两个状态是远远不足以描述很多信息的,所以我们需要很多的0与1不同排列,这样就可以描述更多的信息。
:0与1不能共存,一个位只能放一个状态。(这可不能用薛定谔的猫来解释)。
历史上的常见的计算机位数有8位、16位、32位、64位,现代计算机大多采用的64位。至于为什么没有12位?15位?这个暂时先不管。。。

这样一来,1位,可以描述2个“情形”。2位则可以描述2*2=4个情形,以此类推8位则可以描述2^8,即256个“状态”。
字节Byte:我们将8位记作一个字节。
字Word:我们将16位记作一个字,显然,1个字的占位等于2个字节的占位。
我们点到为止。

这样一来,通过对8位0与1的排列,这256个状态,用来描述英文的26个大小写字母、阿拉伯数字等绰绰有余,而汉字可能有3万多个,所以需要扩充位数,常见的位数中,至少扩充到 16位 方可满足要求,所以你可以这样记,一个字等于16位。(仅仅是提供了一种记忆方法)

计算机,顾名思义,它的一生都在计算。所以,接下来,我们暂时只关注与思考“数字”与“运算”。

二进制

既然计算机只能通过0与1描述这个世界,所以计算机的计数方式是二进制,逢2进一位,而我们人类生来有10个手指,所以计数方式是十进制,逢10进一位。
计算机的字节长度为8位,所以我们一般将二进制前面补充0,直到满足8位,例如:十进制的6,表示为0000 0110

十进制到二进制

数字的起点都是从0开始,我们的数字0到9,则可以在二进制中逐一增加排序依次对应,即从0001001,这根据进制换算,可以很容易理解,至于进制如何换算,可以百度一下,或者我有时间了也总结一下。

真值与机器码

纯数字,计算机可以用二进制表示了,但事实上,我们使用的数字是有正负号的,例如+2,-2.
如果只是无符号的、单独的数字2,用十进制表述为2 D,用二进制表示为:0000 0010 B(这里的D与B用于区分进制,D是十进制的后缀,B代表二进制,为了看清数字,我留了间隔)。
但是-2呢?难不成是-0010 B?这对于人类是可以理解的,但是对于计算机,其中一个位要么是0要么是1,不会出现负号-的,因此,我们约定,最前面的正负号单独占据一位,用 01 表示,所以-2D = 1000 0010B
真值: -000 0010B 这种
机器码:1000 0010B这种,首位的0代表正号,1代表负号。

将代表正负号的位,放在纯数字形成的二进制码的最左边(即,最高位),称作 符号位

为什么讨论0?

本文的案例中,无论是二进制表示、还是计算结果,我都选择了0这个数字。
第一,0 的正负不影响结果,因此,在原码与反码的表示中,0都有两种方式表述。
第二,任何两个数相加的结果,都可以拆解成 “0加上某一个数”。而减法计算中,0的结果很容易引发歧义,因此,解决了0的结果,那么和的结果也就顺利成章的解决了。

正文

原码

按我们的正常思维,符号位+数值位就组成了原码。
原码: 符号位 + 二进制的数值
例如:
0000 0111 表示 +7
1000 0110 表示 -6
……
这没什么问题,很和谐,每个数字都有对应的一个编码,除了0:
0000 0000表示 0,
1000 0000表示 0
这样的话,0具有两种表述情况了,还是分正负的,表面上看去没什么问题,0的正负也的确还是0,如果单单只考虑数字数据在计算机中的存储,这没什么问题。
不过,我们还需要考虑到数字的运算。

加法运算

为了实现数字的计算,所以我们需要进行最简单的加减运算,而实现计算机的减法,涉及到借位,这处理起来比较麻烦,显然,通过加上一个负数实现减法,将减法转变成加法运算,就会使问题简单化了。所以,计算机中二进制的减法运算,就是与负数的加法运算。

不过,举一个很简单的例子,如果计算(+2)+(-2)

0000 0010 :+2
1000 0010 :- 2
1000 0100 :- 4

这里出现了问题,结果并不等于0
所以,,,

反码

如何让上述的结果正确?
我们看到,符号还没有影响结果,但在数值位上,出现了问题。如果这是两个正数相加,例如:2+2在数值上,结果将4,这没问题;所以问题点在于减数,又或者说,问题出现在运算的时候负数的表述上。

反码: 无论正负,符号位不变。对于数值位,正数与原码表示相同,负数的数值位取反。

如果像这样进行编码,正数+负数,结果则为负数(符号位为1),
这样,再次进行计算(+2)+(-2)

0000 0010 :[+2]反
1111 1101 :[- 2]反
1111 1111 :[-0]反

这样的反码表示,正数我们很容易理解,也可以直观计算得出十进制数+2。但是对于负数,因为数值位取反,所以我们需要再取反,反推出负数的十进制结果,这会很让人头痛。

你可能会说:
”我们只在运算的时候进行反码处理,计算结果依旧采用原码进行存储”,这样计算的结果就是:

1000 0000: [-0]原

这样,既保证了加减运算不会出错,结果又很符合本来的思维习惯,这看起来似乎没什么问题啊。
但我们仔细思考,0有两种表示,如果计算0+1呢?这会出现两种情况。

+0-1 的计算:

运算过程:
0000 0000: [+0]反
0000 0001:[+1]反
0000 0001: [+1]反
结果存储:
0000 0001: [+1]原

-0+1 的计算:

运算过程:
1111 1111: [-0]反
0000 0001: [+1]反
0000 0000: [+0]反
结果存储:
0000 0000: [+0]原

这出现了两个结果,使用 +0 的运算结果是正确的,使用-0的结果反而缺少了1。
上述问题的结果出错也不难理解,从反码的表示上看,+0可以由-0进一位得到,所以使用-0计算的结果会比正确的结果少1

我们在测试一下,计算0-1呢?
+0-1的计算:

运算过程:
0000 0000:[+0]反
1111 1110:[-1]反
1111 1110:[-1]反
1000 0001:[-1]原

-0-1的计算:

1111 1111:[-0]反
1111 1110:[-1]反
0000 0001:[+1]反
0000 0001:[+1]原

对比一下,发现使用+0计算是不会出现问题的,所以,无论是在运算还是存储中,0都只使用 +0的形态是不会出现问题的,而-0的意义并不大。
但是,我们不可避免的会遇到运算结果为-0的形态,那该这个-0该怎么办?我们需要对其重新约定。

补码

刚才已经了解到-0的反码进一位便是+0,按照这个思路,针对-0取反码的结果,再加一,就会变成+0的形态,这样就可以继续用于运算与存储了。
这样一来,加法的结果为-0,则自动进一位。我们回忆一下,什么样的运算会导致加法的结果为-0?如果在计算过程中提前进一位,结果是否也就是+0了?
在进行计算(+2)+(-2)的时候,

0000 0010 :[+2]反
1111 1101 :[- 2]反
1111 1111 :[-0]反
结果为-0

这只是个例,对于任何相反数这个结果,都是等价的。我们以这个算式作为例子,权衡一下两个方案的利弊。
首先+0的编码不动它,因为我们的目的就是为了让-0变成+0,即0000 0000依旧表示0
如果对+2的反码进一位,相应的,背后的意思是对所有的正数都进一位,最小的正数本来的0000 0001就会留空,0000 0002则表示1

当然,如果非要保证连贯性,那么对+0也重新定义为0000 0001-0
也将定义为0000 0000,这还是没有消去0存在两种表示这件事。

而如果对-2的反码进一位,相应的,意味着所有的负数都进一位,整个范围内的编码没有留空,并且正数的原有表示也不会改变。这很合乎常规思路。
所以,
补码: 正数的补码与原码一致;负数的补码是该数的反码加1。
所以。补码的规则就解决了反码的漏洞,这样一来,任何两个数的加法都不会出现问题了。(当然,运算结果在范围之内,不考虑溢出的情况)
回想刚才的问题,设想一下,如果运算与存储采用两种方案,运算时用补码,存储时采用原码,这看起来似乎两全其美,因为通过原码我们可以更容易计算出人类熟悉的十进制,而运算的过程我们交给计算机无需过多关注。
但是为什么计算机的存储与运算都采用统一的补码的方案?
斯以为,有这些理由:
第一,使用两种方案,是否意味着计算需要多走两步?(即原码到补码,补码返回原码),这会增加计算时长,影响了计算效率。如果每一次计算都这样,累加起来的亿万次运算将会浪费更久。
第二,我们熟悉的终究是十进制,为了保证可读性,电脑呈现给我们的也只能是十进制,因此我们无需自己去将那些机器码换算成十进制,背后的换算交给计算机即可,这就无所谓编码的形态了。

~最后,为了统一约定,干脆对所有的数都进行统一的编码方案,即 补码。

总结

  1. 原码: 符号位 + 二进制的数值
  2. 反码: 无论正负,符号位不变。对于数值位,正数与原码表示相同,负数的数值位取反。
  3. 补码: 正数的补码与原码一致;负数的补码是该数的反码加1。

补充:
对于这三个理解,还有模运算方面的解释,时钟方面的解释,可以搜索了解,往后有时间的话,也会记录一下…

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值