480

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

因此,我想开始这个社区 wiki帖子来定义什么是二进制补码,如何使用它以及它如何在强制转换(从有符号到无符号,反之亦然)、按位运算和位移运算等操作期间影响数字.

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

4

24 回答 24

685

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

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

它基本上说,

  • 对于零,使用全 0。
  • 对于正整数,开始向上计数,最多为 2 (位数 - 1) -1。
  • 对于负整数,做完全相同的事情,但切换 0 和 1 的角色并倒计时(所以不是从 0000 开始,而是从 1111 开始 - 这是“补码”部分)。

让我们用一个 4 位的 mini-byte 来试试(我们称它为nibble - 1/2 a byte)。

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

这就是我们所能达到的积极程度。2 3 -1 = 7。

对于底片:

  • 1111- 负一
  • 1110- 负二
  • 1101- 负三
  • 1100to 1000- 负四到负八

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

区分正数和负数

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

“符号幅度”负数只是将其正数对应的符号位翻转,但这种方法必须处理将1000(一个1后跟所有0s)解释为“负零”,这是令人困惑的。

“一个的补码”负数只是其正数的位补码,这也会导致与1111(全为)混淆的“负零”。

除非您在硬件附近工作,否则您可能不必处理 Ones 的补码或符号幅度整数表示。

于 2009-06-26T15:29:23.207 回答
369

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

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

首先,考虑一个存储在 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 补码 1010 + 1 = 1011 加 1。因此,二进制补码中的 -5 是 1011。

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

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

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

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

将 1111 转换为十进制:

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

  2. 0000 加 1,得到 0001。

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

  4. 应用符号 = -1。

多田!

于 2009-06-26T15:48:38.563 回答
135

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

回想一下它是如何处理十进制的:
  2345
是一种写法
  2 × 10 3 + 3 × 10 2 + 4 × 10 1 + 5 × 10 0

同样,二进制是一种仅使用01来编写数字的方式,遵循相同的一般思想,但将上面的 10 替换为 2。然后在二进制中,
  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的数字。相应的正数不适合正在使用的位数。

这个数字更奇怪的是,如果你试图通过补加一个来形成它的正数,你会得到相同的负数。零会这样做似乎很自然,但这是出乎意料的,根本不是我们习惯的行为,因为除了计算机之外,我们通常认为数字的无限供应,而不是这种固定长度的算术。

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

于 2012-08-18T20:39:49.260 回答
23

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

于 2013-06-22T19:47:26.417 回答
16

想象一下,您有有限数量的位/trits/digits/whatever。您将 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。

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

十的补码的好处是加法可以正常工作。您不需要做任何特别的事情来添加正数和负数!

于 2009-06-26T19:04:18.720 回答
13

我阅读了jng在 Reddit 上的精彩解释,使用里程表作为类比。

在此处输入图像描述

这是一个有用的约定。如果使用约定,在二进制中加/减正数的相同电路和逻辑运算仍然适用于正数和负数,这就是它如此有用和无所不在的原因。

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

理解这一点对我来说也很困难。一旦我拿到它并回去重新阅读书籍文章和解释(当时没有互联网),结果发现很多描述它的人并没有真正理解它。在那之后我确实写了一本教汇编语言的书(这本书在 10 年里卖得很好)。

于 2018-08-21T16:01:50.370 回答
5

通过将给定数字的第一个补码加一来找到两个补码。假设我们必须找出二进制补码10101然后找到它的补码,即01010添加1到这个结果,即01010+1=01011,这是最终的答案。

于 2010-09-13T13:59:14.800 回答
4

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

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

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

所以 11110100 是 12 的恭维!当你这样想的时候很容易。

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

00001010
11110100
-----------------
11111110  
于 2013-08-28T19:23:12.037 回答
3

从数学的角度来看二进制补码系统真的很有意义。在十的补码中,这个想法本质上是“隔离”差异。

示例:63 - 24 = x

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

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

由于必须从一长串零中减去一个数字的不便情况,我们使用“减少的基数补码”系统,在十进制系统中,九的补码。

当我们看到从一个大的 9 链中减去一个数字时,我们只需要反转这些数字。

示例:99999 - 03275 = 96724

这就是原因,在 9 的补码之后,我们加 1。正如您可能从小时候的数学中知道的那样,9 通过“偷”1 变成 10。所以基本上它只是从差中取 1 的 10 的补码。

在二进制中,二的补码等同于十的补码,而一个的补码等同于九的补码。主要区别在于,我们不是试图用 10 的幂来隔离差异(将 10、100 等添加到等式中),而是试图用 2 的幂来隔离差异。

正是出于这个原因,我们将位反转。就像我们的被减数是十进制的九链一样,我们的被减数是二进制的一链。

示例:111111 - 101001 = 010110

因为 1 的链比 2 的幂次方低 1,所以它们从差值中“窃取”1,就像 9 在十进制中所做的那样。

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

0000 - 0101 = x

1111 - 0101 = 1010

1111 + 0000 - 0101 = x + 1111

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

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

10000 + 0000 - 0101 = x + 10000

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

于 2015-08-26T03:50:54.410 回答
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)给出的例子(见上面的第二个回复)

/* 文森特的开始

将 1111 转换为十进制:

数以 1 开头,所以是负数,所以求 1111 的补码,即 0000。0000 加 1,得到 0001。将 0001 转换为十进制,即 1。应用符号 = -1。多田!

文森特结束 */

应该理解为

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

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

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

于 2016-10-14T19:06:13.523 回答
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

这种能力允许使用 negate then add 进行有效的二进制减法。a - b = a + (-b)

取 1 的补码的官方方法是从 1 中减去每个数字的值。

        1'scomp(0101) = 1010.

这与单独翻转或反转每个位相同。这导致负零不受欢迎,因此将 1 添加到 te 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 sum 0101 sum 000101

减法:

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

请注意,在使用 2 的补码时,数字左侧的空白区域用零填充正数,但用 1 填充负数。进位总是被添加,并且必须是 1 或 0。

干杯

于 2019-04-13T23:21:57.573 回答
3

2 的补码本质上是一种求出二进制数的加法倒数的方法。问问自己:给定一个二进制形式的数字(存在于固定长度的内存位置),当添加到原始数字(在固定长度的内存位置)时,什么样的位模式会使结果全为零?(在相同的固定长度内存位置)。如果我们能想出这个位模式,那么那个位模式将是原始数字的 -ve 表示(加法逆);根据定义,将一个数字添加到其加法逆元总是会导致零。示例:取 5,即 101 存在于单个 8 位字节中。现在的任务是提出一个位模式,当添加到给定的位模式 (00000101) 时,将导致用于保存此 5 的内存位置全为零即字节的所有8 位都应为零。为此,从最右边的 101 位开始,对于每个单独的位,再次问同样的问题:我应该在当前位上添加什么位以使结果为零?考虑到通常的结转,继续这样做。在我们完成了最右边的 3 个位置(定义原始数字而不考虑前导零的数字)之后,最后一个进位进入加法逆的位模式。此外,由于我们将原始数字保存在单个 8 位字节中,加法逆运算中的所有其他前导位也应该是 1,因此(这很重要)当计算机添加“数字”(使用 8位模式)及其使用“那个”存储类型(一个字节)的加法逆运算该字节中的结果将全为零。

 1 1 1
 ----------
   1 0 1
 1 0 1 1 ---> additive inverse
  ---------
   0 0 0
于 2019-08-08T02:12:20.390 回答
2

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

于 2009-06-26T15:49:10.530 回答
2

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

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

于 2012-06-20T09:10:41.073 回答
2

2 的补码:当我们用一个数字的 1 的补码添加一个额外的数字时,我们将得到 2 的补码。例如:100101 它的 1 的补码是 011010,2 的补码是 011010+1 = 011011(通过将 1 与 1 的补码相加)有关更多信息 ,本文以图形方式解释。

于 2014-11-26T09:29:03.227 回答
1

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

  1. 避免 0 的多重表示
  2. 为了避免在溢出的情况下跟踪进位位(如在一个补码中)。
  3. 执行简单的操作,如加法和减法变得容易。
于 2017-10-16T19:57:46.410 回答
1

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

让我们考虑这个例子,

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

x = 5;

这表示为0101

x = -5;

当计算机遇到-符号时,它会计算它的 2 的补码并存储它。 i.e5 = 0101,它的 2 的补码是1011.

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

  1. 如果第一位是,1那么它必须是negative数字。
  2. 如果除第一位之外的所有位都是0正数,因为没有-0数字系统。(1000 is not -0而是正数8
  3. 如果所有位0都是0.
  4. 否则它是一个positive number.
于 2019-10-26T17:21:25.167 回答
1

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

于 2019-12-13T17:31:39.167 回答
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;
于 2016-12-30T18:31:29.293 回答
0

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

于 2019-01-07T15:04:29.947 回答
0

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

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

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

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

对于一个温和的介绍,精彩的 Computerphile 制作了一个关于这个主题的视频

于 2019-02-15T20:42:45.293 回答
0

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

于 2021-05-09T03:09:31.710 回答
-2

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

于 2016-09-22T00:33:42.240 回答
-6

最简单的答案:

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

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

于 2015-09-25T17:31:01.820 回答