什么是“2的补充”?

时间:2009-06-26 15:21:39

标签: binary bit-manipulation computer-science twos-complement data-representation

我正在使用计算机系统课程并且正在努力,部分地使用Two's Complement。我想了解它,但我读过的所有内容并没有为我提供图片。我已阅读wikipedia article和其他各种文章,包括my text book

因此,我想开始这个社区wiki 帖子,以定义Two's Complement是什么,如何使用它以及它如何在演员阵营(从签名到无签名,反之亦然)等操作中影响数字,逐位操作和位移操作。

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

24 个答案:

答案 0 :(得分:576)

两个补码是一种存储整数的聪明方法,因此常见的数学问题很难实现。

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

基本上说,

  • 为零,使用全0。
  • 对于正整数,开始向上计数,最大值为2 (位数 - 1) -1。
  • 对于负整数,执行完全相同的操作,但切换0和1的角色(因此不是从0000开始,而是从1111开始 - 这是“补充”部分)。

让我们尝试使用4位的迷你字节(我们称之为nibble - 1/2个字节)。

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

我们可以采取积极的态度。 2 3 -1 = 7。

负面消息:

  • 1111 - 否定一个
  • 1110 - 否定二
  • 1101 - 否定三
  • 11001000 - 负4至负8

请注意,您为负数(1000 = -8)获得了一个额外的值,而不是肯定的。这是因为0000用于零。这可以视为计算机的Number Line

区分正数和负数

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

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

答案 1 :(得分:317)

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

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

首先考虑以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 +
----

另一个系统是excess notation。你可以存储负数,你摆脱了两个零问题,但加法和减法仍然很困难。

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

将十进制转换为二进制补码

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

  2. 如果数字为正数,那么您就完成了。 例如5是使用二进制补码表示法的二进制0101。

  3. 如果数字是负数,那么

    3.1找到补码(反转0和1)  例如-5是0101,因此找到补码是1010

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

  4. 那么,如果你想在二进制文件中做2 +( - 3)怎么办? 2 +( - 3)为-1。 如果您使用符号幅度来添加这些数字,您需要做什么? 0010 + 1101 =?

    使用二进制补码考虑它是多么容易。

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

    将两个补码转换为十进制

    将1111转换为十进制:

    1. 数字以1开头,所以它是负数,所以我们找到了1111的补码,即0000。

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

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

    4. 应用sign = -1。

    5. 多田!

答案 2 :(得分:111)

与我见过的大多数解释一样,上面的解释清楚如何使用2的补码,但没有真正解释他们 数学上的东西。我会尝试这样做,至少对整数而言,我将首先介绍一些可能很熟悉的背景。

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

以同样的方式,二进制是一种使用 0 1 编写数字的方法,遵循相同的一般想法,但用2替换上面的10。然后在二进制中, 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 < / sup> + 1 ×2 3 + 1×2 2 + 1 ×2 1 < / sup> + 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 。相应的正数不符合正在使用的位数。

这个数字更奇怪的是,如果你试图通过补充和添加一个来形成积极的,你会得到相同的负数。看起来很自然零会做到这一点,但这是出乎意料的并且根本不是我们习惯的行为,因为除了计算机,我们通常会想到无限制的数字,而不是这种固定长度的算术。

这就像是一个奇怪的冰山一角。在表面之下还有更多的等待,但这对于这次讨论来说已经足够了。如果研究定点算术的“溢出”,你可能会找到更多。如果你真的想进入它,你也可以研究“模运算”。

答案 3 :(得分:18)

2的补码对于找到二进制的值非常有用,但是我想到了解决这个问题的更简洁的方法(从未见过其他人发布过它):

取二进制,例如:1101 [假设空格“1”是符号]等于 -3

使用2的补码我们会这样做...翻转1101到0010 ...添加0001 + 0010 ===&gt;给我们0011. 0011 in positive binary = 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 < sup> 0 * 1)= -3

答案 4 :(得分:14)

想象一下,你有一个有限数量的位/ trits / digits /无论如何。您将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。

此表示是十的补充。计算机通常使用二进制补码,除了使用位而不是数字之外,它们是相同的。

关于十个补码的好处是添加正常工作。您不需要做任何特殊的事情来添加正数和负数!

答案 5 :(得分:5)

通过添加给定数字的第1至第1个补码找出两个补语。 让我们说我们必须找出10101的两个补码,然后找到它的补码,即010101添加到此结果,即01010+1=01011,这是最后的答案。

答案 6 :(得分:4)

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

我们需要将12的赞美部分从10中减去。 二进制12是00001100。 二进制10是00001010。

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

所以11110100是12的赞美!当你这么想的时候很容易。

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

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

答案 7 :(得分:3)

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

enter image description here

  

这是一个有用的约定。相同的电路和逻辑运算   在二进制中加/减正数对两个正数仍然有效   和负数(如果使用约定),这就是为什么   有用且无所不在。

     

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

     

这对我来说也很难。一旦我明白了,然后回到   重新阅读书籍中的文章和说明(没有互联网   当时),结果很多人描述它并不是真的   理解。我确实写了一本书教汇编语言   那个(十年来确实卖得很好)。

答案 8 :(得分: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 - &gt; 011(减少(一个)基数补充)

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

011 +1 - &gt; 100(两个补码)。

现在有了这个新的理解,让我们来看看给出的例子 Vincent Ramdhanie(见上文第二回复)

/ *文森特的开始

将1111转换为十进制:

数字从1开始,所以它是负数,所以我们找到1111的补码,即0000。 加1到0000,我们获得0001。 将0001转换为十进制,即1。 应用sign = -1。 多田!

文森特的结尾* /

应理解为

数字从1开始,所以它是负数。所以我们知道它是某个值x的二进制补码。为了找到由它的二进制补码表示的x,我们首先需要找到它的1的补码。

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

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

答案 9 :(得分:3)

从数学的角度来看这两个补码系统真的很有意义。在十个补充中,我们的想法是基本上“孤立”&#39;差异。

示例:63 - 24 = x

我们添加24的补充,实际上只是(100 - 24)。实际上,我们所做的只是在等式的两边增加100。

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

由于不得不从一长串零中减去一个数字的不方便,我们使用了一个减少的基数补码&#39;系统,在十进制系统中,九个补码。

当我们从一大串九的数据中减去一个数字时,我们只需要反转这些数字。

示例:99999 - 03275 = 96724

这就是为什么,在九个补语之后,我们加1.你可能从童年时期的数学中知道,9通过偷窃&#39;因此,基本上它只是十个补充,差异为1。

在Binary中,两个补码相当于十个补码,而九个补码是九个补码的补码。主要区别在于,我们不是试图将差异与10的幂相加(将10,100等加到等式中),而是试图将差异与2的幂相隔离。

正因为如此,我们将这些位反转。就像我们的minuend是十进制的九连串一样,我们的minuend是一个二进制的链。

示例:111111 - 101001 = 010110

因为一连串低于2的优势,他们会偷走&#39;差异为1,如九,十进制。

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

<00> 0000 - 0101 = x

1111 - 0101 = 1010

1111 + 0000 - 0101 = x + 1111

为了隔离&#39; x,我们需要加1,因为1111与10000相距1,我们删除了前导1,因为我们只是将它添加到原始差异中。

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

10000 + 0000 - 0101 = x + 10000

只需从两侧移除10000即可获得x,即它的基本代数。

答案 10 :(得分:2)

2的补充:当我们添加一个额外的1和1的补码时,我们将获得2的补码。例如:100101它的1的补码是011010,2的补码是011010 + 1 = 011011(通过添加1的1的补码)For more information 本文以图形方式解释。

答案 11 :(得分:2)

这是一种巧妙的编码负整数的方法,使得数据类型的大约一半位组合被保留用于负整数,并且大多数负整数与其相应的正整数的相加导致进位溢出使结果为二进制零。

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

答案 12 :(得分:1)

几个星期前我遇到了同样的问题。我最后从各种来源在线阅读它,尝试将各个部分放在一起,并自己写一下,以确保我理解正确。我们使用两个补码主要有两个原因:

  1. 避免多次表示0
  2. 为了避免在发生溢出的情况下跟踪进位(如在一个补码中)。
  3. 执行加法和减法等简单操作变得容易。
  4. 如果您想更详细地解释手头的问题,请尝试我撰写的文章here。希望它有所帮助!

答案 13 :(得分:1)

我喜欢lavinio的答案,但是移位会增加一些复杂性。通常在尊重符号位或不遵守符号位时可以选择移动位。这是将数字视为带符号(半字节为-8到7,字节为-128到127)或全范围无符号数(半字节为0到15,字节为0到255)之间的选择。

答案 14 :(得分:1)

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

Service

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

                  35=035=000000035.

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

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

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

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

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

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

        1'scomp(0101) = 1010.

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

添加:
        1110携带111110携带          0110与000110相同          1111 111111     总和0101总和000101

摘要:

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

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

欢呼

答案 15 :(得分:0)

问题是'什么是“2 的补码”?'对于那些想要从理论上理解它的人的简单答案(我试图补充其他更实际的答案):2 的补码是对偶系统中负整数的表示,不需要额外的字符,例如 + 和 -。

答案 16 :(得分:0)

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

答案 17 :(得分:0)

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

让我们考虑这个例子,

计算机使用Binary Number System代表任何数字。

x = 5;

这表示为0101

x = -5;

当计算机使用-符号时,它会计算它的2的补码并将其存储。 i.e 5 = 0101,它的2的补数是1011

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

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

答案 18 :(得分:0)

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

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

答案 19 :(得分:0)

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

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

零操作的行为在不对正零和负零进行特殊处理的情况下有利于给出零的单一表示。 00001111的补码,当添加1时。溢出到0000,给我们一个零,而不是一个正数和一个负数。

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

作为一个简短的介绍,出色的Computerphile制作了video on the subject

答案 20 :(得分:0)

给定数字的

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

答案 21 :(得分:0)

参考:https://www.cs.cornell.edu/~tomf/notes/cps104/twoscomp.html

我反转所有位并添加1.编程:

  // in C++11
  int _powers[] = {
      1,
      2,
      4,
      8,
      16,
      32,
      64,
      128
  };

  int value=3;
  int n_bits=4;
  int twos_complement = (value ^ ( _powers[n_bits]-1)) + 1;

答案 22 :(得分:-2)

您还可以使用在线计算器计算十进制数的二进制补码二进制表示:http://www.convertforfree.com/twos-complement-calculator/

答案 23 :(得分:-6)

最简单的答案:

1111 + 1 =(1)0000。所以1111必须是-1。然后-1 + 1 = 0。

完全理解这些对我而言。