计算机为什么要使用补码,原码、反码和补码为何物?

本文深入解析计算机为何使用补码,探讨原码、反码和补码的概念及其数学依据,揭示补码在二进制运算中的作用,同时介绍byte的取值范围及补码的计算方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、为何产生这些疑问

最近在研究字符集的相关内容,在Java中发现汉字在gbk和utf8的编码下的byte都存在负数的情况,一下子勾起了我的好奇心,查阅了各种资料寻求答案,最终把我的目光吸引到了计算机存储数据的底层实现上,原来计算机存储的数据都是数据的补码。

2、数学依据

原码、反码、补码是面向计算机的表达方式。对于人的直观来讲是不太好理解的(除了原码)。
我们先抛开这三个名词,从生活中去慢慢发现他们的形象。我们在生活中使用的数字是十进制的,钟表可以看做是十二进制和六十进制。我们就冲这三个经常接触的事出发,去一探究竟。
首先要明白数学上的三个概念,模、同余和互补。
:其实可以简单的理解为一个范围,超出这个范围以后回到原点,即为一个轮回。比如十进制的一个轮回就是10,到10以后就是0,因为一位数只能表示最多到9。那么这个范围就是10。
同余:字面意思就是相同的余数,当然余数相同的前提是在同一个模。A数加上或者减去一个模的整数倍得到的数都为A数的同余数。比如:在钟表中,2点的同余数是14点。
互补:互补也是在相同的模M前提下。模M减去A数得到的B数即为A数的互补数。即:A+B = M。

3、数学运算探索

我们来看一个十进制的运算:
7-5=2;根据人的运算能力这种计算很容易得出结果,但是计算机为了控制底层电子电路的复杂度,减法运算的实现就需要更多的逻辑电路去支持,显然是一件不友好的事情。那我们继续往下看,能不能把减法运算用加法实现呢?
这时候,补数就有了用武之地:
7-5 -> 7 +(10-5)-> 7+5 = 12;结果不对呀,但是你仔细观察两次的结果的关系就会发现神奇的事情,12和2是相对于10的同余数。其实到这里已经可以说明问题了,但是我当初理解到这里并没有明白将减法变成,被减数加上减数的补数的加法是可行的,因为两次结果虽然是同余数,但是到底还是两个不同的数,如何得到正确的结果?我们继续。
聪明的人可能已经有答案了,那就是把高位的1舍去不就是正确答案吗?那为什么舍去高位?这又会是一个问题:
与其说舍去高位不如说舍去一模M,再举一个例子,这次我们把模M设为30:
12-10 -> 12+20 = 32;舍去模M结果是2,正确结果。
可能很多人对于设置模M还是很困惑,其实你可以看一下前面我给模M的描述的时候就说了,可以理解为一个范围一个轮回。为什么要有这个限制,往后看,你就明白了。

3、byte的取值范围

现在我们进入计算机的二进制世界:
接下来做的运算都是在一byte(字节)能够表示的数字的范围内。因为计算机中存储有效数据的最小单位为byte(字节)。没错这个byte就是模M,一个byte是由8个bit(二进制位)组成的,8位能表示最多256个数,由于计算机中需要表示不同符号的数,所有byte的最高位是用来表示符号的,所以真正用来存储数字的只要7bit,那7bit能表示的数就是128个,那byte的模M也就是128。高出128的数都是无法表示的包括128在内。
高位表示符号即:1为负数,0为正数,00000000表示十进制的+0,那10000000表示十进制的-0吗?从上面的表述来看似乎没有问题,但是计算机设计的时候就已经意识到这个问题了1byte能表示256个数,如果出现两个0(因为+0和-0表示的都是0)的话那么就白白浪费了一个数据位。其实10000000表示的是-128(00000000其实在128的模M中和128都是0,那么10000000在不能表示-0的情况下表示-128就顺理成章了),所以byte能表示的数为[-128,127]。

4、原码、反码、补码的定义

进入正题,来了解一下原码、反码和补码。
原码:二进制正真要表示的数字;
反码:原码的各个二进制位取反得到的数(0变1,1变0,如:00110100的反码为11001011) ;
补码:原码的各个二进制位取反之得到的数加1,即反码加1,(正数的补码就是原码,负数的补码高位的1不变);
知其然不知其所以然,很多人都是死记硬背这个概念和定义,但如果你是刚接触,过一段时间你可能就会忘记这些定义的其中一部分。

5、补码定义解疑

上面的三个定义,原码和反码没有必要过多解释,但是,补码码的计算方式很多人是困惑的,为什么要各位取反再加1?
回到前面的互补数,我们知道,互补数,假设一个数A,求A在模M下的补数=M-A;同理,一个二进制数B,求它的补码(负数的情况下)=M-B,其实和互补数的求法是一样的,那为什么是取反后加1呢?我们直接用二进制数来看一下:
M = 10000000;
B = 10000001;
我们知道byte的高位是符号位不表示数字,那么这个模M(128)显然是不能用来计算的,但是,没关系我们可以用1111111(127,M-1)来替代模M,那么前面的计算就可以是M-1-B+1,这样间接计算B的补数,这样一来127的二进制低7位各个位都是1,减去一个小于128的数就好比这个数各位取反,由于我们把模M的值低估了1所有取反之后再加1就刚好是B在128模M下的补数,即补码。
说到这里应该都明白补码定义的根本了。

6、总结

为了计算的方便,计算机中的数据都以补码的形式存储(正数的补码就是原码),便于理解我们可以把补码理解为在一个固定模M下原数的补数,只是这里只对负数的无符号数有效。Byte的长度是8bit,高位的一个bit用来表示符号1为符号,0为正号。Byte表示的整数范围[-128,127],在Java中int类型用4个byte即32bit表示模M为2的32次方,int取值[-(2的32次方),2的31次方]。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值