补码的原理

本文深入探讨了计算机中补码的概念及其在负数运算中的作用,通过一个面试题详细解释了补码计算过程。博主通过对比数学与计算机存储的区别,阐述了补码如何解决减法问题,并介绍了二进制加法的实现算法,揭示了计算基础知识背后的智慧。

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

怎么就想到这里了

看了一道面试题目,如下所示:

byte b = (byte)129;
System.out.println(b);

问输出结果。

这个题目考察了四个知识点:

  1. 整数的字面量默认是 int 类型的。而 int 类型占 32 个 bit 。
  2. byte 类型的是 8 ge bit 。能够表示的最大正整数是 127.
  3. 补码的计算公式。
  4. 强制类型转换

129 = 2 的 7 次方 + 1
所以在内存中的表示为: 0000 0000 0000 0000 0000 0000 1000 0001
byte 强转后 : 1000 0001
前面的 16 位都已经消失了。 我们看到最高位是一个 1 ,说明这是一个负数。
负数,我们知道计算机里面,负数是用补码表示的,所以这东西可不是表示的 -1
它表示的是某个负数的补码。例如我们现在有个负数了 A ,那 A的补码 = ~A +1.
现在我们有补码了所以需要 ~(A的补码 -1) = A ,做这样的一个操作。计算的过程如下所示:

我们先将符号去掉
                 补码 -1000 0001 - 000 0001 = 000 0000
                   取反:                             111 1111
111 1111 换算成 10 进制:27- 1 = 127
所以答案是 -127

本人一直搞不清楚补码这个东西是怎么来,就像知道π这么个常量,我们却不知道它是怎么的出来的。
于是乎一顿查资料。整理了大概的思路。给大家的整理一下。

补码的原理

在学习计算组成的时候,就死记硬背,补码是为了将加减乘除都转成加法而设计的。
这样计算机只要解决加法的逻辑,其他的运算只要转变一下就可以使用加法了。
例如,减法可以转成加上一个负数,乘法可以转成 n 个相同的数相加,除法可以降为减法。
在这个基础上,就可以实现更加复杂的运算了。

所以重点就在于如何实现的负数的加法。要想知道补码的原理,还是要从计算的存储说起。
我们知道计算存储数据是一个固定位数,就拿 Java 里面的 byte 来说吧,它是 8 位的。
我们想一个问题,从 0 开始,我一直加 1 ,当加到第 2 的 8 次方的时候,是不是又
回到 0 了。

如下面的代码:

byte bb = 0 ;
for (int i = 0; i < Math.pow(2, 8); i++) {
    bb++;
    System.out.println(String.format("第 %s 次加1 , bb 的值为: %s" , i+1 , bb));
}

从这里面我们可以知道,存储位数固定回导致数据循环出现。这也是计算机和数学的区别。
在数学上,数字就只是一个数字,它不占用任何的内存空间。它是存在于人们大脑的抽象的概念。
但是到了计算里面,一个数字真真实实保存在一个磁盘的 bit 。

接着来说,我写上面这个循环的意义。不知道您看每看过刘慈欣的小说《诗云》,里面神级文明为了写出想李白一样
意境的诗文,准备使用排列组合的办法枚举出所有文字的组合,然后再从里面挑出意境高远的诗句。他做这件事情之前
给人类解释说,如果想到河的对岸有两种办法,一个是涉水过河,另外一个办法从对岸的反方向走,终有一天你会走到
河的对岸,因为地球是球型的。讲这个和我们说的补码有什么关系呢?请看下图,

在这里插入图片描述

A - B 的们可以看成是从 A 点逆时针往走 B 的长度。但是计算里面存储结构式有周期性,好比上图的圆形,我们可以从
反方向顺时针走到 B 点。

在这里插入图片描述

我们知道补码=负数取反 + 1 . 取反式对数字部分取泛,不包括符号位。我们知道一个二进制的数字,最高位上的 1 代表了
数字位 max,剩余的位上所有的 1 ,加起来等于 max-1。例如:

我们以 4 bit 为例子。

1111 代表 10 进制的 15。最高位上的那个 1 对应的 10 进制是  8(1000),其余的 1 对应的 10 进制是 7 (0111)

由上面的图我们可以看出来,从反方向,A 到 B 点走的路程是 ,AC + CB,其中 CB = (2max-1)/2。AC= (2max-1)/2 - AB。
而且 AB 就是我们减去的那个正数的长度。

我们知道负数的反码最高已经是 1 了,所以假设我们从 A 点出发,已经顺时针走过了 AC 的长度,接下来就是 AC 的长度呢?其实就是 max-1 - AB ,也就从剩下所以 1 里面拿掉 AB 所代表那些 1. 举个例。

还是以 4 个 bit 为例子:

-1 的原码是 1001,后三位是数字位,111 - 001 就是 AC 的长度。正好就是 001 取泛的操作的。也就是我说的从剩下所以 1 里面拿掉 AB 所代表那些 1

计算机的加减乘除

计算机是如何进行加减乘除的呢?其实都是转化为两个数字的加法来实现。例如,

A - B 可以转化为 A + (-B),A * B 可以转变为 A 个 B 相加 ,A/B 可以转为 A - B 可以减多少次,减到结果为 0 为至。所以这样看来只要实现两个数据的加法,其他的计算都可以有加法计算出结果。

两个正整数相加的算法:

    public static int plusTwoInteger(int a , int b){
        int rs =  a ^ b;
        int mid = a & b ;
        int rs_buff = 0 ;
        while(mid != 0){
            rs = (rs_buff = rs) ^ ( mid = mid << 1);
            mid = rs_buff & mid  ;
        }
        return rs ;
    }

异或得 1 的位置上说明是 1 和 0组合,相加后就变成了 1,不需要进位,并且的 1 说明,两个数相同位置上都是 1 ,需要进 1 位,我们有 进位操作符号。让再让进 1 之后的数据和做异或的数据做并且操作,看看结果是不是等于 0 ,如等于 0 ,则说明可以停止计算了。所以只需要使用,异或、并且、向左移位、循环着四种级别计算机操作,就能实现加法的计算。实际上计算的加法器就是这样实现的。

感叹

说到这里不尽感叹一些,那些我们习以为常的知识是多么充满智慧。那些研究出计算机的脑洞得有多大才能想出使用补码来解决减法问题的。

### 原码、反码和补码的概念 #### 定义与表示方法 原码是最简单的机器数表示法。最高位是符号位,“0”代表正数,“1”代表负数,其余各位则用来表示数值大小[^1]。 对于8位二进制数来说: - 正数的原码就是其对应的二进制数; - 负数的原码则是先写出该数绝对值的二进制形式,在最左边加上“1”。 例如,`+7` 的原码为 `0000 0111`; `-7` 的原码为 `1000 0111`。 #### 反码定义及其特点 当处理的是正整数时,它的反码与其原码相同;而对于负整数而言,除了符号位外,其他各位置上的比特都要取反(即由原来的‘0’变为‘1’, 或者相反),形成所谓的“按位求反”。因此: - 对于上述例子中的 `-7`, 首先得到其原码 `1000 0111`,接着除开首位不变之外对其余部分逐位反转获得最终的结果 `1111 1000` 即为其反码[^2]。 #### 补码概念及优势说明 为了简化加减运算并提高效率,实际应用中更倾向于采用补码来表达带符号的数据。具体做法如下所示: - 如果是一个正数,则直接将其转换成相应的二进制序列作为补码- 若为负数的话,那么就基于此数的反码再加一即可得出对应补码。 继续以上面提到的例子来看,`-7` 的补码计算过程可以描述为:首先获取到它按照前述规则形成的反码 `1111 1000` ,之后再加上一位变成 `1111 1001` 。值得注意的是,在这种情况下,即使存在溢出也不会影响结果的有效性,因为额外产生的高位会被自动丢弃掉。 ```cpp // C++ code to demonstrate conversion from decimal to binary and then finding one's complement (反码) of a negative number. #include <iostream> using namespace std; string decToBin(int n){ string bin = ""; while(n>0){ int rem=n%2; char ch=rem+'0'; bin+=ch; n/=2; } reverse(bin.begin(),bin.end()); return bin.empty() ? "0":bin; } void findOnesComplement(string &binaryString){ for(char& c : binaryString){ if(c=='0')c='1'; else if(c=='1')c='0'; } } int main(){ int num=-7; unsigned int absNum=(num<0)?(-num):num; string binRep=decToBin(absNum); cout << "Binary Representation: "<<(num>=0?"":"1")<<binRep<<"\n"; if(num<0){ findOnesComplement(binRep); cout<<"One's Complement:"<<'1'+binRep<<"\n"; // Adding '1' before the reversed bits represents sign bit as per ones-complement rule. } } ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值