7

尽管我读过很多文章说大多数 2 的补码用于表示有符号整数中的负数,这是最好的方法,

但是由于某种原因,我将这个(下)卡在了我的脑海中,并且在不知道它的历史的情况下无法摆脱它

“使用有符号整数时,使用前导位 1 表示负数。”

我在网上和 StakOverflow 中阅读了许多帖子,认为 2 的补码是表示负数的最佳方式。但我的问题不是关于最佳方式,而是关于历史或“领先位”概念从哪里出现然后消失?

PS:也不是我,很多其他人也对此感到困惑。

编辑 - 1 我提到的所谓的前导 1 方法在这篇文章中用一个例子来描述: 为什么用二进制补码来表示负数?

现在我明白了,1 的 MSB 表示负数。这本质上是 2 的补码,而不是任何特殊方案。

例如。如果不是第一位,我们不能说 1011 代表 -5 还是 +11。

感谢:jamesdlin、Oli Charlesworth、Lister 先生提出恳求的问题,让我意识到正确的答案。

咆哮:我认为有很多团体/人们被教导或被认为(错误地)认为 1011 评估为 -3。1 表示 - 和 011 表示 3。

那些问“我的问题是什么……”的人可能从他们第一次学习到正确的 2 补码方式就被教导了,并且没有接触到这些错误的答案。

4

5 回答 5

4

有符号整数的二进制补码表示有几个优点。

现在让我们假设 16 位。

0 到 32,767 范围内的非负数在有符号和无符号类型中具有相同的表示。(Two's-complement 与 one's-complement 和 sign-and-magnitude 共享此功能。)

二进制补码很容易在硬件中实现。对于许多操作,您可以对有符号和无符号算术使用相同的指令(如果您不介意忽略溢出)。例如,-1 表示为1111 1111 1111 1111,+1 表示为0000 0000 0000 0001。如果将它们相加,忽略高位是符号位这一事实,数学结果是1 0000 0000 0000 0000; 删除除低 16 位之外的所有位,得到0000 0000 0000 0000,这是正确的有符号结果。解释与unsigned相同的操作,您正在添加65535 + 1和 getting 0,这是正确的无符号结果(环绕模 65536)。

您可以将前导位视为“符号位”,而只是另一个值位。在无符号二进制表示中,每个位表示 0 或 1 乘以位值,总和是这些乘积的总和。最低位的位置值为 1,下一个低位为 2,然后是 4,依此类推。在 16 位无符号表示中,高位的位置值为32768。在 16 位有符号二进制补码表示中,高位位的值为-32768。尝试几个例子,你会发现一切都很好。

有关更多信息,请参阅维基百科

于 2012-05-03T06:19:23.690 回答
2

这不仅仅是领先地位。这是关于所有的位。

从加法开始

首先让我们看看如何在 4 位二进制中对 2 + 7 进行加法:

  10 + 
 111
____
1001

和十进制长加法一样:一点一点,从右到左。

  • 在最右边的地方,我们将 0 和 1 相加,得到 1,没有进位。
  • 在右数第二位,我们将 1 和 1 相加,即十进制的 2 或二进制的 10 - 我们写 0,携带 1。
  • 在右数第三位,我们将携带的 1 与已经存在的 1 相加,得到二进制 10。我们写入 0,携带 1。
  • 刚拿到的 1 写在右数第四位。

长减法

现在我们知道二进制 10 + 111 = 1001,我们应该能够倒推并证明 1001 - 10 = 111。同样,这与十进制长减法完全相同。

1001 -
  10
____
 111

这是我们所做的,再次从右到左工作:

  • 在最右边的地方,1 - 0 = 1,我们把它写下来。
  • 其次,我们有 0 - 1,所以我们需要多借一点。我们现在做二进制 10 - 1,剩下 1。我们把它写下来。
  • 第三,记住我们借了一个额外的位 - 所以我们又得到了 0 - 1。我们使用相同的技巧来借一个额外的位,得到 10 - 1 = 1,我们把它放在结果的第三位。
  • 第四,我们又要处理一个借来的问题。从已经存在的 1 中减去借来的位:1 - 1 = 0。我们可以将其写在结果前面,但由于这是减法的结束,因此没有必要。

有一个小于零的数字?!

你还记得你是如何学习负数的吗?部分想法是,您可以从任何其他数字中减去任何数字,仍然可以得到一个数字。所以 7 - 5 是 2;6 - 5 为 1;5 - 5 为 0;什么是4-5?好吧,推理这些数字的一种方法是简单地应用与上述相同的方法进行减法。

例如,让我们尝试二进制的 2 - 7:

     10 -
    111
_______
...1011

我以与以前相同的方式开始:

  • 在最右边的地方,从 0 中减去 1,这需要借位。10 - 1 = 1,所以结果的最后一位是 1。
  • 在最右边的第二个位置,我们有 1 - 1 和一个额外的借位,所以我们必须减去另一个 1。这意味着我们需要借自己的位,得到 11 - 1 - 1 = 1。我们在第二个最右边的位置。
  • 排在第三位,上面的数字没有更多的位了!但是我们知道我们可以假装前面有一个 0,就像我们会在底部的数字用完比特时做的那样。所以我们有 0 - 1 - 1 因为从第二位借位。我们得再借一点!无论如何,我们有 10 - 1 - 1 = 0,我们将其写在右数第三位。
  • 现在发生了一件非常有趣的事情——减法的两个操作数都没有数字了,但我们还有一个借位需要处理!哦,好吧,让我们继续我们一直在做的事情。我们有 0 - 0,因为这里顶部或底部操作数都没有任何位,但由于借位,它实际上是 0 - 1。

    (我们又要借了!再这样借下去,很快就要宣布破产了。)

    无论如何,我们借位,我们得到 10 - 1 = 1,我们写在右数第四位。

现在任何一个有半点想法的人都会看到,我们将继续借用钻头,直到奶牛回家,因为没有更多的钻头可以四处走动了!如果你忘了的话,我们在两个地方之前用完了它们。但如果你试图继续前进,它看起来像这样:

...00000010
...00000111
___________
...11111011
  • 在第五位我们得到 0 - 0 - 1,我们借位得到 10 - 0 - 1 = 1。
  • 在第六位,我们得到 0 - 0 - 1,我们借位得到 10 - 0 - 1 = 1。
  • 在第七位我们得到 0 - 0 - 1,我们借位得到 10 - 0 - 1 = 1。

......所以它继续在你喜欢的许多地方。 顺便说一句,我们刚刚导出了 -5 的二进制补码形式

您可以对您喜欢的任何一对数字尝试此操作,并生成任何负数的二进制补码形式。如果您尝试执行 0 - 1,您会明白为什么 -1 表示为...11111111. 您还将意识到为什么所有二进制补码负数的最高有效位都为 1(原始问题中的“前导位”)。

实际上,您的计算机并没有无限多的位来存储负数,因此它通常会在某个更合理的数字之后停止,例如 32。我们如何处理位置 33 中的额外借位?呃,我们只是默默地忽略它,希望没有人注意到。当有人注意到我们的新数字系统不起作用时,我们称之为整数溢出

最后的笔记

当然,这不是使我们的数字系统工作的唯一方法。毕竟,如果我欠你 5 美元,我不会说你目前与我的余额是 ...999999995 美元。

但是我们刚刚推导出的系统有一些很酷的东西,比如减法可以在这个系统中给出正确的结果,即使你忽略了其中一个数字是负数的事实。通常情况下,我们必须考虑条件步骤的减法:要计算 2 - 7,我们首先必须弄清楚 2 小于 7,因此我们计算 7 - 2 = 5,然后在前面加上一个减号得到 2 - 7 = -5。但是对于二进制补码,我们只是继续做减法,而不关心哪个数字更大,正确的结果会自己出来。其他人提到加法很好用,乘法也很好用。

于 2012-05-03T08:15:32.370 回答
1

每个人说,你不使用前导位。例如,在一个 8 位有符号字符中,

11111111

代表-1。您可以测试前导位以确定它是否为负数。

使用 2 的补码有很多原因,但最重要的是方便。取上面的数字并加 2。我们最终得到什么?

00000001

您基本上可以免费加减 2 的补码。这在历史上是一件大事,因为逻辑很简单;您不需要专用硬件来处理签名号码。你使用更少的晶体管,你需要更简单的设计等等。它可以追溯到 8 位微处理器之前,它甚至没有内置乘法指令(甚至许多 16 位微处理器都没有,例如65c816 用于苹果 IIe 和 Super NES)。

话虽如此,乘法与 2 的补码也相对微不足道,所以这没什么大不了的。

于 2012-05-03T06:13:49.837 回答
1

补码(包括十进制的 9 补码、机械计算器/加法机/收银机)一直存在。例如,在具有四个十进制数字的九进制补码中,0000..4999 范围内的值是正数,而 5000..9999 范围内的值是负数。有关详细信息,请参阅http://en.wikipedia.org/wiki/Method_of_complements

这直接产生二进制的 1s 补码,并且在 1s 和 2s 补码中,最高位充当“符号位”。这并不能准确解释计算机是如何从一个补码变为二进制补码的(顺便说一下,在将这些单词拼写为带撇号的单词时,我使用了 Knuth 的撇号约定)。我认为这是运气,对“负零”的刺激,以及二进制补码需要结束进位的方式(相对于二进制补码,不需要它)的组合。

从逻辑上讲,使用哪个位来表示符号并不重要,但出于实际目的,使用最高位和二进制补码可以简化硬件。在晶体管价格昂贵的时候,这非常重要。(甚至管子,尽管我认为大多数(如果不是全部)真空管计算机都使用补码。无论如何,它们比 C 语言早了很多。)

总而言之,历史可以追溯到电子计算机和 C 语言之前,当从机械计算器转换到真空管 ENIAC 再到晶体管计算机,然后再到“芯片”、MSI、LSI、VLSI 等。

于 2012-05-03T07:50:57.297 回答
0

好吧,它必须这样工作才能2 plus -2给出零。早期的 CPU 有硬件加法和减法,有人注意到通过补全所有位(一个补码,原始系统)来改变值的“符号”,它允许现有的加法硬件正常工作——除了有时结果为负零。(-0 和 0 有什么区别?在这样的机器上,它是不确定的。)

很快有人意识到,通过使用二进制补码(通过反转位并加一来将数字在负数和正数之间转换),可以避免负零问题。

所以实际上,受负数影响的不仅仅是符号位,而是除 LSB 之外的所有位。但是,通过检查 MSB,可以立即确定有符号值是否为负。

于 2012-05-03T06:20:41.573 回答