什么是“ 2的补码”?


434

我正在上计算机系统课程,并且在某种程度上一直在努力Two's Complement交流。我想了解它,但是我读过的所有内容都没有为我带来帮助。我已经阅读了维基百科文章和其他各种文章,包括我的教科书

因此,我想开始这个社区Wiki帖子,以定义Two's Complement是什么,如何使用它以及它如何在诸如强制类型转换(从有符号到无符号,反之亦然),按位操作和移位操作等操作期间影响数字。

我希望有一个清晰简洁的定义,使程序员容易理解。

Answers:


627

二进制补码是一种存储整数的聪明方法,因此常见的数学问题非常容易实现。

要理解,您必须考虑二进制数。

它基本上说,

  • 对于零,请使用全0。
  • 对于正整数,开始递增计数,最大值为2 (位数-1) -1。
  • 对于负整数,请执行完全相同的操作,但请切换0和1的作用(因此,与其以0000开头,不以1111开头-这是“补码”部分)。

让我们尝试使用4位的小字节(我们将其称为半字节-1/2字节)。

  • 0000 -零
  • 0001 -一个
  • 0010 -两个
  • 0011 -三
  • 01000111-四到七

这是我们可以肯定的。2 3 -1 = 7。

对于底片:

  • 1111 -负数
  • 1110 -负二
  • 1101 -负三
  • 11001000-负四至负八

请注意,对于负数(1000= -8),您会得到一个额外的价值,而对于正数,则不会。这是因为0000用于零。这可以视为计算机的编号行

区分正数和负数

这样做,第一位具有“符号”位的作用,因为它可用于区分非负十进制值和负十进制值。如果最高有效位是1,那么二进制可以说是负数,而最高有效位(最左边)是0,则可以说十进制值是非负数。

“一个补码”负数只是翻转符号位,然后从0开始计数。但是这种方法必须处理解释1000为“负零”的情况,这令人困惑。通常,您只需要在靠近硬件的情况下担心这一点。


146
二进制补码的最好部分可能是它如何简化数学。尝试将2(0010)和-2(1110)加在一起,得到10000。最高有效位是溢出,因此结果实际上是0000。几乎像魔术一样,2 + -2 =
0。– Naaff

96
除了易于加减之外的另一个优点是2s补码只有一个零。如果您使用简单的符号位,例如使用0001表示+1,使用1001表示-1,则将有两个零:0000(“ +0”)和1000(“ -0”)。后面真的很痛苦。
约尔格W¯¯米塔格

26
支持这一点,并解释为什么负值的范围比正值的范围大。我来寻找范围差异的原因。
Ashwin

2
您不应该说“对于负整数,做完全相同的事情,但要倒数并切换0和1的角色”
Koray Tugay 2015年

1
太棒了。添加了将位转换为负整数的额外部分。
Suraj Jain

339

我想知道是否可以比维基百科的文章更好地解释它。

您试图用二进制补码表示法解决的基本问题是存储负整数的问题。

首先考虑存储在4位中的无符号整数。您可以拥有以下内容

0000 = 0
0001 = 1
0010 = 2
...
1111 = 15

这些是未签名的,因为没有迹象表明它们是负数还是正数。

符号幅度和多余符号

要存储负数,您可以尝试多种方法。首先,您可以使用符号幅度符号,该符号符号将第一位分配为符号位以表示+/-,其余位分配为幅度。因此,再次使用4位并假设1表示-,而0表示+,则

0000 = +0
0001 = +1
0010 = +2
...
1000 = -0
1001 = -1
1111 = -7

那么,您看到那里的问题了吗?我们有正负0。更大的问题是加和减二进制数。使用符号幅度进行加法和减法的电路将非常复杂。

什么是

0010
1001 +
----

另一个系统是 多余的符号。您可以存储负数,摆脱了两个零的问题,但是加法和减法仍然很困难。

随之而来的是二进制补码。现在,您可以存储正整数和负整数,并相对轻松地执行算术运算。有很多方法可以将数字转换为二进制补码。这是一个

将小数转换为二进制补码

  1. 将数字转换为二进制(现在忽略符号),例如5为0101,-5为0101

  2. 如果数字为正数,则说明您已经完成。例如5是二进制补码表示法的0101。

  3. 如果数字为负,则

    3.1找到补码(将0和1求反),例如-5是0101,所以找到补码是1010

    3.2将1加到补码1010 +1 =1011。因此,二进制补码中的-5是1011。

那么,如果您想以二进制形式进行2 +(-3)怎么办?2 +(-3)为-1。如果您使用符号幅度将这些数字相加,该怎么办?0010 + 1101 =?

使用二进制补码考虑一下它会多么容易。

 2  =  0010
 -3 =  1101 +
 -------------
 -1 =  1111

将Two的补码转换为十进制

将1111转换为十进制:

  1. 数字以1开头,因此为负数,因此我们找到了1111的补数,即0000。

  2. 将1加到0000,我们得到0001。

  3. 将0001转换为十进制数1。

  4. 应用符号= -1。

多田!


45
我认为最好的答案。
Koray Tugay,2015年

5
是的,这很简单,并且很好地说明了这一点
Max Koretskyi

3
我不明白转换两种方式时加一总是导致相同的数字。在我看来,您可以颠倒这些步骤,或者减去一个或多个。
Marcos Pereira

2
为什么要在补数上加1?
紫南杏

4
此答案应在Wikipedia上使用。
弘树

119

就像我看到的大多数解释一样,上面的解释很清楚如何使用2的补码,但是并没有真正解释它们的含义。 数学上。我将尝试这样做,至少对于整数,我将介绍一些可能首先熟悉的背景。

回想一下十进制的工作方式:
  2345
是一种写
  2 ×10 3 + 3 ×10 2 + 4 ×10 1 + 5 ×10 0的方式

同样,二进制是遵循相同的一般思想而仅使用01写入数字的方式,但是用2s代替了上面的10s。然后以二进制形式,
  1111
是一种写
  1 ×2 3 + 1 ×2 2 + 1 ×2 1 + 1 ×2 0的方法
,如果计算出来,结果等于15(以10为底)。那是因为它是
  8 + 4 + 2 + 1 = 15。

对于正数来说,这一切都很好。如果您愿意在负数前面加上一个减号,那么它甚至适用于负数,就像人类对十进制数所做的那样。这甚至可以在计算机上完成,但自1970年代初以来我还没有见过这样的计算机。我将保留其他讨论的理由。

对于计算机来说,使用补码会更有效对负数表示。这是经常被忽略的东西。补码表示法涉及数字的某种反转,甚至是正常正数之前的隐含零。这很尴尬,因为出现了问题:所有人?这可能是要考虑的无数个数字。

幸运的是,计算机不代表无限。数字被限制为特定的长度(或宽度,如果您愿意)。因此,让我们返回具有特定大小的正二进制数。在这些示例中,我将使用8位数字(“位”)。因此我们的二进制数实际上是
  00001111

  0 ×2 7 + 0 ×2 6 + 0 ×2 5 + 0 ×2 4 + 1 ×2 3 +1×2 2 + 1 ×2 1 + 1 ×2 0

为了形成2的补数负数,我们首先对所有(二进制)数字进行补数以形成
  11110000,
然后将1
  加成以形成11110001,
但是我们如何理解这意味着-15?

答案是我们更改了高位(最左边的一位)的含义。对于所有负数,该位将为1。的变化将是改变其到它出现在数的值贡献的符号所以现在我们的。11110001应理解为代表
  - 1 ×2 7 + 1 ×2 6 + 1 ×2 5 + 1 ×2 4 + 0 ×2 3 + 0×2 2 + 0 ×2 1 + 1 ×2 0
注意表达式前面的“-”吗?这意味着符号位的权重为-2 7,即-128(以10为底)。所有其他位置保持与未签名二进制数字相同的权重。

算出我们的-15,它是
  -128 + 64 + 32 + 16 + 1
在您的计算器上尝试一下。是-15。

在我看到负数出现在计算机中的三种主要方式中,2的补码会赢得通用性,以方便使用。不过,这很奇怪。由于它是二进制的,因此必须有偶数个可能的位组合。每个正数可以与它的负数配对,但是只有一个零。否定零将使您零。因此,还有另一种组合,即符号位为1且数字为0的数字其他所有位置均为。相应的正数将不适合所使用的位数。

关于这个数字,更奇怪的是,如果您尝试通过补码和加数来形成它的正数,则会得到相同的负数。零看起来很自然,但这是意料之外的,完全不是我们习惯的行为,因为除了计算机之外,我们通常认为数字的供应不受限制,而不是这种固定长度的算法。

这就像是一堆奇怪的冰山一角。在表面之下还有更多的等待,但这足以进行讨论。如果研究定点算术的“溢出”,可能会发现更多信息。如果您真的想研究它,您可能还会研究“模块化算术”。


1
我喜欢这个答案!说明采用2s补码和加一的方法。
SJ。

我也喜欢这个答案。特别是在显示负数的位置。在这里,我认为整个数字不仅是MSB,而且是反向的,然后又添加了其他加权值。谢谢,这解决了我的大脑障碍
user188757 '17

提到没有反序的奇数数字,做得好。但是我们该怎么办?如果有人试图反转溢出标志,我们是否只是设置溢出标志?
NH。

虽然其他答案都集中在“如何”上,但这个答案却使我们轻而易举地了解了“为什么”。它帮助了我。谢谢!
Abhishek Pathak

如果数字以11000 ... 000结尾,则将其取反将产生01000 ... 000。二进制补码表示法是这样的思想,即最左边表示的数字左侧的所有数字都应具有与该数字相同的值,但是当将表示形式为1000 ... 000的数字求逆时,这将是不正确的。
超级猫

20

2的补码对于查找二进制值非常有用,但是我想到了一种更简洁的方法来解决此类问题(从未见过其他人发表过):

以二进制文件为例:1101 [假设空格“ 1”是符号”等于-3

使用2的补码,我们将执行此操作...将1101翻转到0010 ...加0001 + 0010 ===>给出我们0011.0011正二进制= 3,因此1101 = -3

我意识到:

而不是进行所有的翻转和加法运算,您只需做求解正二进制的基本方法即可(假设0101)为(2 3 * 0)+(2 2 * 1)+(2 1 * 0)+(2 0 * 1)= 5。

做一个完全相反的概念!

以1101为例:

对于第一个数字而不是2 3 * 1 = 8,请执行-(2 3 * 1)= -8

然后照常继续,执行-8 +(2 2 * 1)+(2 1 * 0)+(2 0 * 1)= -3


1
最好的方法是,我可以理解2的补码。读完这篇文章,我可以理解上述问题的所有答案。
Shakeel Shahzad 2015年

1
在《计算机系统:程序员的观点》一书中提到了这种方法。
jimo 2015年

1
这是一个更快的方法!
chanzerre

14

想象一下,您拥有有限的位数/技巧/数字/其他。您将0定义为所有数字均为0,然后自然向上计数:

00
01
02
..

最终,您将溢出。

98
99
00

我们有两个数字,可以代表0到100之间的所有数字。所有这些数字都是正数!假设我们也要表示负数吗?

我们真正拥有的是一个周期。2之前的数字是1。1之前的数字是0。0之前的数字是... 99

因此,为简单起见,假设50以上的任何数字均为负。“ 0”到“ 49”代表0到49。“ 99”是-1,“ 98”是-2,...“ 50”是-50。

这个表示是十的补码。计算机通常使用二进制补码,除了使用位代替数字外,其余都是相同的。

十进制补码的优点是加法有效。您无需执行任何特殊操作即可添加正数和负数!


9

我以里程表为类比,阅读了jng 关于Reddit的精彩解释。

在此处输入图片说明

这是一个有用的约定。如果使用约定,则在二进制中加/减正数的相同电路和逻辑运算仍可在正数和负数上工作,这就是为什么它是如此有用且无所不在的原因。

想象一下汽车的里程表,它在(例如)99999处滚动。如果增加00000,则得到00001。如果减小00000,则得到99999(由于滚转)。如果将99999加1,则返回00000。因此确定99999表示-1非常有用。同样,确定99998表示-2非常有用,依此类推。您必须停在某个地方,并且按照惯例,数字的上半部分将被视为负数(50000-99999),而下半部分的正数将代表自己(00000-49999)。结果,高位数字为5-9表示所代表的数字为负数,高位数字为0-4表示所代表的数字为正数-与表示二进制补码二进制数的最高位完全相同。

我也很难理解这一点。一旦获得并返回以重新阅读书籍文章和解释(那时还没有互联网),结果发现很多描述它的人并不真正理解它。在那之后,我确实写了一本教汇编语言的书(这本书卖了十年很好)。


5

通过在给定数字的1'至1'补码上加两个补码。假设我们必须找出二进制补码,10101然后找到其补码,即01010添加1到该结果中,即01010+1=01011,这就是最终答案。


4

让我们使用8位二进制形式获得10 – 12的答案:我们真正要做的是10 +(-12)

我们需要得到12的补余部分,以从10中减去它。二进制的12为00001100。二进制的10为00001010。

要获得12的补码部分,我们只需反转所有位,然后加1。二进制反转的12就是11110011。这也是反码(一个补码)。现在我们需要添加一个,现在是11110100。

所以11110100是12的补充!这样想起来很容易。

现在您可以以二进制形式解决以上10-12的问题。

00001010
11110100
-----------------
11111110  

3

2的补码:当我们在数字的1的补码上加上一个额外的数字时,我们将得到2的补码。例如:100101,它的1的补码为011010,而2的补码为011010 + 1 = 011011(通过将1与1的补码相加)。有关更多信息, 本文以图形方式进行了说明。



3

从数学的角度来看两者的补码系统确实很有意义。用十的补语,想法是本质上“隔离”差异。

例如:63-24 = x

我们加上24的补数,实际上是(100-24)。所以说真的,我们要做的是在方程式的两边加100。

现在的等式是:100 + 63-24 = x + 100,这就是为什么我们删除100(或10或1000或其他值)的原因。

由于必须从零位长链中减去一个数字的不便之处,我们使用了“减基数补码”系统,在十进制中使用了9的补码。

当我们看到从九个大数字链中减去的数字时,我们只需要反转数字即可。

例如:99999-03275 = 96724

这就是为什么在9的补码之后加1。您可能从童年数学中就知道,“偷” 1会使9变成10。因此,基本上只是10的补码使差值减为1。

在Binary中,二的补码等于十的补码,而一的补码与九的补码相等。主要区别在于,我们没有尝试将幂乘以10(将10、100等添加到方程式中),而是试图将幂乘以2。

出于这个原因,我们将位反转。就像我们的minuend是十进制的9的链一样,我们的minuend是二进制的1的九的链。

例如:111111-101001 = 010110

因为1的链比1的2的幂小1,所以它们像9的十进制一样从差中“窃取” 1。

当我们使用负二进制数时,我们实际上只是在说:

0000-0101 = x

1111-0101 = 1010

1111 + 0000-0101 = x + 1111

为了“隔离” x,我们需要加1,因为1111与10000相距1,并且我们删除了前导1,因为我们只是将其添加到原始差值中。

1111 + 1 + 0000-0101 = x + 1111 + 1

10000 + 0000-0101 = x + 10000

只需从两边都除去10000即可得到x,这是基本的代数。


3

到目前为止,许多答案很好地解释了为什么使用二进制补码来表示负数,但没有告诉我们二进制补码是什么,尤其是为什么不添加“ 1”,实际上却常常以错误的方式添加。

混乱来自对补数定义的理解不充分。补码是可以使某些内容完整的缺失部分。

根据定义,基数b中n位数字x的基数补码为b ^ nx。在二进制4中用100表示​​,它有3个数字(n = 3)和基数2(b = 2)。因此其基数补码为b ^ nx = 2 ^ 3-4 = 8-4 = 4(或二进制为100)。

但是,在二进制运算中,获得基数补码并不像获得其减小的基数补码那样容易,后者的定义为(b ^ n-1)-y,仅比基数补数小1。要减少基数补码,只需翻转所有数字即可。

100-> 011(小数补码)

要获得基数(二进制)补数,我们只需添加1作为定义的定义。

011 +1-> 100(二进制补码)。

现在有了这种新的理解,让我们看一下Vincent Ramdhanie给出的示例(请参见上面的第二个答复)

/ * Vincent的开始

将1111转换为十进制:

数字以1开头,因此为负数,因此我们找到了1111的补数,即0000。将0000加1,得到0001。将0001转换为十进制,即为1。应用符号= -1。多田!

文森特结束* /

应该理解为

数字以1开头,因此为负数。因此,我们知道它是某个值x的二进制补码。要找到以2的补码表示的x,我们首先需要找到其1的补码。

x的补码:1111 x的补码:1111-1-> 1110; x = 0001,(翻转所有数字)

应用符号-,答案= -x = -1。


3

补语一词源自完整性。在十进制世界中,数字0到9提供数字或数字符号的补码(完整集)来表示所有十进制数字。在二进制世界中,数字0和1提供数字的补码来表示所有二进制数字。实际上,必须使用符号0和1来表示所有内容(文本,图像等)以及正数(0)和负数(1)。在我们的世界中,数字左侧的空白被视为零:

                  35=035=000000035.

在计算机存储位置中没有空白空间。所有位(二进制数字)必须为0或1。为了有效使用内存,可以将数字存储为8位,16位,32位,64位,128位表示形式。将存储为8位数字的数字传输到16位位置时,符号和大小(绝对值)必须保持相同。1的补码和2的补码表示均有助于此操作。作为一个名词:1的补码和2的补码都是有符号数的二进制表示,其中最高有效位(左侧的一位)是符号位。0代表正,1代表负。 2s补码并不意味着负面。这表示已签名的数量。与小数一样,幅度表示为正数。当升级到具有更多位的寄存器[]时,该结构使用符号扩展来保留数量:

       [0101]=[00101]=[00000000000101]=5 (base 10)
       [1011]=[11011]=[11111111111011]=-5(base 10)

作为动词:2的补语表示取反。这并不意味着使负面。这意味着如果否定就变成积极;如果为正,则为负。大小是绝对值:

        if a >= 0 then |a| = a
        if a < 0 then |a| = -a = 2scomplement of a

此功能允许使用取反然后加法进行有效的二进制减法。a-b = a +(-b)

取1的补码的正式方法是为每个数字减去1的值。

        1'scomp(0101) = 1010.

这与分别翻转或反转每个位相同。这将导致负零,这个负零不受欢迎,因此在te 1的补码上加1可以解决该问题。要取反或取2s补码,请先取1s补码,然后加1。

        Example 1                             Example 2
         0101  --original number              1101
         1's comp  1010                       0010
         add 1     0001                       0001
         2's comp  1011  --negated number     0011

在示例中,否定符也适用于符号扩展号。


加法:1110进位111110进位0110与000110 1111 111111总和0101总和000101相同

减去:

    1110  Carry                      00000   Carry
     0110          is the same as     00110
    -0111                            +11001
  ----------                        ----------
sum  0101                       sum   11111

请注意,在使用2的补数时,数字左侧的空白表示正数为零,而负数表示为1。进位总是加进,必须为1或0。

干杯


3

2的补码本质上是提出二进制数的加法逆的一种方法。问问自己:给定二进制格式的数字,当将哪种位模式添加到原始数字后,结果将为零?如果可以提出该位模式,则该位模式为原始数字的-ve表示(加反)。因为按照定义,在其加和逆数上加一个数字应始终为零。示例:以101(十进制为5)为例。现在的任务是提出一个位模式,当将其添加到给定的位模式(101)时将得出零。为此,请从101的最右边开始,并针对每个单独的位,再次询问相同的问题:我应在“此”位上加上哪一位以使结果为零?考虑到通常的结转,继续这样做。在完成最右边的3个位置(定义原始数字的数字而不考虑前导零)之后,最后一个进位进入加法逆的位模式。此外,由于我们可以将原始数字保持为单个字节,因此加法逆的所有其他前导位也应为1,这样当计算机使用“ that”存储类型(字符)将数字及其加法逆相加时该char中的结果将全部为零。

 1 1 1
 ----------
   1 0 1
 1 0 1 1 ---> additive inverse
  ---------
   0 0 0

2

我喜欢lavinio的回答,但移位位会增加一些复杂性。通常,在尊重符号位或不尊重符号位的情况下,都可以选择移动位。您可以在将数字视为带符号的数字(半字节为-8至7,字节为-128至127)或全范围无符号数字(半字节为0至15,字节为0至255)之间进行选择。


2

这是一种巧妙的编码负整数的方法,数据类型的位组合的大约一半保留给负整数,大多数负整数与它们对应的正整数相加会导致进位溢出将结果保留为二进制零。

因此,在2的补码中,如果1为0x0001,则-1为0x1111,因为这将导致总和为0x0000(溢出为1)。


1

几周前我遇到了同样的问题。我最终从各种来源在线阅读了有关它的内容,试图将它们拼凑在一起,然后亲自撰写有关它的内容,以确保我正确地理解了它。我们使用二进制补码主要有两个原因:

  1. 避免0的多重表示
  2. 为了避免在溢出时跟踪进位(如补码)。
  3. 进行简单的操作(例如加法和减法)变得容易。

如果您想对问题进行更详细的说明,请尝试在此处撰写我的文章。希望能帮助到你!




0

给定数字的2的补码为no。将1与1的补码相加得到。假设我们有一个二进制数:10111001101它的1的补码是:01000110010它的2的补码是:01000110011


0

按位补码是对数字中的所有位进行翻转。为了补全它,我们翻转所有位并加一个。

对有符号整数使用2的补码表示形式,我们应用2的补码运算将正数转换为负数,反之亦然。因此,以半字节为例,0001(1)变为1111(-1)并再次应用op,返回到0001

零操作的行为有利于给出零的单一表示,而无需对正零和负零进行特殊处理。0000与互补1111,当加1时。溢出到0000,给我们一个零,而不是一个正数和一个负数。

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

作为一个简短的介绍,出色的Computerphile制作了有关该主题视频


0

简而言之,2's Complement是一种在计算机内存中存储负数的方法。而正数存储为普通二进制数。

让我们考虑这个例子,

计算机Binary Number System用来表示任何数字。

x = 5;

表示为0101

x = -5;

当计算机encouters -签名时,它将计算为2的补码并将其存储。 i.e5 = 0101,它的2的补码是1011

计算机用来处理数字的重要规则是,

  1. 如果第一位是数字,1则必须为negative数字。
  2. 如果除第一位以外的所有位都是0正数,因为没有-0数字系统(1000 is not -0而是正数8)。
  3. 如果所有位都00
  4. 否则它是一个positive number


-6

最简单的答案:

1111 + 1 =(1)0000。因此1111必须为-1。然后-1 + 1 = 0。

完全了解这些对我来说是完美的。


这不能为问题提供答案。要批评或要求作者澄清,请在其帖子下方发表评论。
Codor)

是答案。最简单的。对我来说-最好的一个。
德米特里(Dmitry)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.