“二进制补码”是什么?

505

我正在参加一门计算机系统课程,其中一部分内容让我感到困难的是二进制补码。我想理解它,但是我读过的所有资料都没有给我一个清晰的结构图。我阅读了维基百科文章和其他各种文章,包括我的教科书

什么是二进制补码,我们如何使用它,以及它如何在操作(如从有符号到无符号的强制转换,位运算和位移操作)中影响数字?


1
我认为对我有帮助的评论是,补码类似于反码,但不是给出 0 而是给出 2^N(根据定义),例如对于数字 A 的 3 位,我们想要 A+~A=2^N 所以 010 + 110 = 1000 = 8,这是 2^3。至少这解释了在这里“补码”一词的含义,它不仅仅是将 01 的含义取反。MIT 有用的视频:https://www.youtube.com/watch?v=RbJV-g9Lob8 - Charlie Parker
一个快速的记忆法和澄清混淆的方法:就像符号幅度表示法一样,二进制补码表示法也有一个“符号位”。因此,要找到二进制补码表示法的有符号(负数、零或正数)数字的值,只需计算符号位,即最高有效位为负,然后其余位将按照通常的方式计算(作为无符号编码中的正数)。感谢布赖恩特先生和奥哈洛伦先生撰写了令人惊叹的书籍《计算机系统:程序员的视角》(注:这本书远不止这个简单的示例)。 - aderchox
24个回答

3
2 的补码实质上是一种计算二进制数加法反元素的方法。你可以这样问自己:在一个固定长度的内存位置上给定一个二进制数,什么比特模式加上原始数(在相同的固定长度内存位置上),使得结果全为零?如果我们能想出这个比特模式,那么该比特模式就是原始数的负表示(加法反元素),因为根据定义,将一个数与其加法反元素相加总是会得到零。例如:取5,它是以单个8位字节形式呈现的101。现在的任务是想出一种比特模式,当添加到给定的比特模式(00000101)时,会导致用于存放此5的内存位置全部为零,即字节的所有8位都应该是零。为了做到这一点,从101最右边的比特开始,并针对每个单独的比特,再次问自己同样的问题:应该添加哪个比特才能使结果为零?继续这样做,考虑通常的进位。在完成了最右边的3个位置后(不考虑前导零定义原始数字的数字),最后的进位进入加法反元素的比特模式中。此外,由于我们将原始数字保存在单个8位字节中,加法反元素中的所有其他前导比特也应该是1,这样(很重要)当计算机使用“该”存储类型(一个字节)将数字(用8位模式表示)和它的加法反元素相加时,在“那个字节”的结果将会全部为零。
 1 1 1
 ----------
   1 0 1
 1 0 1 1 ---> additive inverse
  ---------
   0 0 0

3
迄今为止,许多答案很好地解释了为什么要使用二进制补码表示负数,但并没有告诉我们什么是二进制补码数字,特别是为什么要添加“1”,事实上,通常以错误的方式添加。
混淆来自对补码数字定义的理解不足。补码是使某物完整的丢失部分。
在基数为b的n位数字x中,基数补码的定义为b^n-x。
在二进制中,4表示为100,它有3个数字(n=3)和基数为2(b=2)。因此,它的基数补码是b^n-x = 2^3-4=8-4=4(或100在二进制中)。
但是,在二进制中,获得基数的补码并不像获得其减小的基数补码那样容易,后者被定义为(b^n-1)-y,比基数补码少1。要获得减小的基数补码,只需翻转所有数字。
100 -> 011(减小(一的)基数补码)
要获得基数的(二的)补码,我们只需根据定义加1。
011 +1 -> 100(二进制补码)。
现在有了这个新的理解,让我们来看一下 Vincent Ramdhanie 给出的例子(请参见上面的第二个回复):
将 1111 转换为十进制: 该数字以 1 开头,因此它是负数,所以我们找到 1111 的补码,即 0000。 将 1 添加到 0000 中,我们得到 0001。 将 0001 转换为十进制,即 1。 应用符号 = -1。 Tada!
应理解为: 该数字以 1 开头,因此它是负数。因此,我们知道它是某个值 x 的二进制补码。为了找到由其二进制补码表示的 x,我们首先需要找到它的一进制补码。
x 的二进制补码:1111 x 的一进制补码:1111-1 -> 1110; x = 0001(翻转所有数字)
应用符号 -,答案为 -x = -1。

2
2的补码:当我们在一个数的1的补码上再加上额外的1时,就会得到这个数的2的补码。例如:100101的1的补码是011010,而2的补码是011010+1=011011(通过在1的补码上加1来获得)。了解更多信息,本文以图形方式进行解释。

只为具有解释和圆圈的链接加1。 - Manohar Reddy Poreddy

2

我喜欢lavinio的答案,但是移位操作会增加一些复杂性。通常有两种选择:在保留符号位的情况下移动位数或者不考虑符号位移动位数。这就是将数字视为带符号数(半字节为-8到7,字节为-128到127)或全范围无符号数(半字节为0到15,字节为0到255)之间的选择。


2

2的补码是一种聪明的负整数编码方式,大约半数的数据类型位组合被保留用于负整数,并且大多数负整数与其对应正整数相加会导致进位溢出而结果为二进制零。

因此,在2的补码中,如果一个数是0x0001,则-1是0x1111,因为这将导致组合和为0x0000(带有1的溢出)。


2

二进制补码是表示负数的一种方式,大多数控制器和处理器都以二进制补码形式存储负数。


1
这与其他答案提供的信息无关。 - Adrian Mole
“Controller” 的意思是 微控制器 吗? - Peter Mortensen
这是对于我所在公司或团队中95%的人提出这个问题的最佳答案,因为二进制补码是一个实现细节,大多数好奇的开发者今天不需要过多关注,就像学习pn结掺杂或三角函数的泰勒级数一样。所有更长的答案都应该在附加说明之前先说这一点,然后再解释额外细节纯粹是为了那些真正需要了解其架构原因的人。 - Ahmed Fasih

1

二进制补码主要用于以下几个原因:

  1. 避免0的多重表示。
  2. 在溢出的情况下,避免像一补码中那样跟踪进位位。
  3. 执行简单的加减法等操作变得容易。

“像加法和减法这样的简单操作变得容易了。”:以什么方式? - Peter Mortensen
通过使用相同的机制(电路)处理加法和减法,使得计算变得更简单。加法30 + 40 = 70 可以通过对 +30+40 的二进制表示进行加法运算来完成。减法40 - 30 = 10,可以通过对 (+40) + (-30) 进行加法运算来完成,而 (-30) 的二进制表示可以通过反转 (+30) 的二进制表示的位并加上 1 来获得。 - K.N. Bhargav

1
简单来说,二进制补码是一种在计算机内存中存储负数的方法。而正数则以普通二进制数的形式存储。
让我们看一个例子,计算机使用二进制数系统表示任何数字。
x = 5;

这表示为0101
x = -5;

当计算机遇到-符号时,它会计算其二进制补码并存储它。
也就是说,5 = 0101,其二进制补码为1011
计算机处理数字的重要规则如下:
  1. 如果第一个位是1,那么它必须是一个负数
  2. 如果除了第一个位以外的所有位都是0,那么它是一个正数,因为在数值系统中没有-01000不是-0,而是正数8)。
  3. 如果所有位都是0,那么它是0
  4. 否则它是一个正数

0
给定数字的二进制补码是将该数字的 ones' complement与1相加得到的数字。
假设我们有一个二进制数:10111001101
它的1's补码是:01000110010
它的二进制补码是:01000110011

0

对一个数字进行按位补码是翻转其中的所有位。要将其进行二进制补码,我们翻转所有位并加一。

对于有符号整数,使用二进制补码表示法,我们应用二进制补码运算将正数转换为其负等价物,反之亦然。因此,以半字节为例,0001(1)变成了1111(-1),再次应用该操作,返回到0001

该操作在零处的行为有利于给零提供单个表示,无需对正零和负零进行特殊处理。0000补码为1111,加1后溢出为0000,给我们一个零,而不是一个正零和负零。

这种表示法的一个关键优点是,对于无符号整数的标准加法电路,在应用它们时会产生正确的结果。例如,在半字节中加1和-1:0001 + 1111,位溢出寄存器,留下0000

如果您想进行初步了解,可以观看优秀的Computerphile制作的有关该主题的视频


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接