为什么对签名数字更喜欢两个补码而不是符号和数量?

时间:2009-07-14 13:15:08

标签: binary math twos-complement negative-number internal-representation

我只是好奇是否有理由为了在二进制中表示-1,使用二进制补码:翻转位并加1?

-1由11111111(二进制补码)表示,而不是(对我来说更直观)10000001,它是二进制1,第一位为负标志。

免责声明:我不依赖二进制算术来完成我的工作!

19 个答案:

答案 0 :(得分:308)

这样做是为了添加不需要任何特殊的逻辑来处理负数。查看the article on Wikipedia

假设您有两个数字,2和-1。用你的“直观”方式表示数字,它们分别是00101001(我坚持4位大小)。在两者的补充方式中,它们是00101111。现在,假设我想添加它们。

两个补码的添加非常简单。您可以正常添加数字,最后的任何进位都将被丢弃。所以他们添加如下:

  0010
+ 1111
=10001
= 0001 (discard the carry)

0001为1,这是“2 +( - 1)”的预期结果。

但是在你的“直观”方法中,添加更复杂:

  0010
+ 1001
= 1011

哪个是-3,对吗?在这种情况下,简单的添加不起作用。您需要注意其中一个数字是否定的,如果是这种情况,则使用不同的算法。

对于这种“直观”的存储方法,减法是与添加不同的操作,需要在添加之前对数字进行额外检查。由于您希望最基本的操作(加法,减法等)尽可能快,您需要以允许您使用最简单算法的方式存储数字。

此外,在“直观”存储方法中,有两个零:

0000  "zero"
1000  "negative zero"

直观上相同的数字,但存储时有两个不同的值。每个应用程序都需要采取额外的步骤来确保非零值也不是负零。

以这种方式存储整数还有另外一个好处,那就是当你需要扩展寄存器的宽度时,存储的值。使用二进制补码,将一个4位数存储在一个8位寄存器中是一个问题重复其最重要的一点:

    0001 (one, in four bits)
00000001 (one, in eight bits)
    1110 (negative two, in four bits)
11111110 (negative two, in eight bits)

只需要查看较小单词的符号位并重复它,直到它填充较大单词的宽度。

使用您的方法,您需要清除现有位,这是一个额外的操作,除了填充:

    0001 (one, in four bits)
00000001 (one, in eight bits)
    1010 (negative two, in four bits)
10000010 (negative two, in eight bits)

在这两种情况下,您仍然需要设置额外的4位,但在“直观”的情况下,您还需要清除第5位。这是每个应用程序中最基本和最常见操作之一的一小步。

答案 1 :(得分:18)

Wikipedia说明了一切:

  

二进制补码系统的优点是不需要加法和减法电路检查操作数的符号来确定是加或减。该属性使系统更易于实现,并且能够轻松处理更高精度的算术。此外,零只有一个表示,避免了与负零相关的细微差别,它存在于补充系统中。

换句话说,添加是相同的,无论数字是否为负数。

答案 2 :(得分:12)

即使这个问题已经过时了,我也要花2美分。

在我解释之前,让我们回到基础。 2'补码是1的补码+ 1。 现在什么是1的补充,另外还有什么意义。

任何n位数及其1的补码之和为您提供可由这些n位表示的最高数字。 例如:

 0010 (2 in 4 bit system)
+1101 (1's complement of 2)
___________________________
 1111  (the highest number that we can represent by 4 bits)

现在,如果我们尝试在结果中再添加1个,会发生什么。它会导致溢出。

结果将是1 0000为0(因为我们正在使用4位数字,(左边的1是溢出)

所以,

Any n-bit number + its 1's complement = max n-bit number
Any n-bit number + its 1'complement + 1 = 0 ( as explained above, overflow will occur as we are adding 1 to max n-bit number)

然后有人决定将1的补码+ 1称为2'补码。所以上面的陈述变成: 任何n位数+ 2的补数= 0 这意味着2的数字补码= - (在那个数字中)

所有这些产生了另外一个问题,为什么我们只能使用n位中的(n-1)来表示正数,为什么最左边的第n位表示符号(最左边的位表示0 + ve数,和1表示-ve number)。例如,为什么我们只使用java中int的前31位表示正数,如果第32位为1,则为-ve数。

 1100 (lets assume 12 in 4 bit system)
+0100(2's complement of 12)
___________________________

1 0000(结果为零,进位1溢出)

因此,(n + 2'实现n)= 0的系统仍然有效。这里唯一的歧义是2的补码12是0100,它除了在2s补码系统中代表-12之外,它还含糊不清+8。

如果正数在最左边的位中始终为0,则会解决此问题。在这种情况下,他们的2的补码在最左边的位总是有1,并且我们不会有同一组位的模糊性,代表2的补码数和+ ve数。

答案 3 :(得分:8)

Two's complement允许以正常方式完成加法和减法(就像你为无符号数字缠绕一样)。它还可以防止-0(用一种比较数字的正常逐位方法表示0不会等于0的单独方式)。

答案 4 :(得分:6)

这是为了简化数字的总和和差异。在2的补语中编纂的负数和正数之和与以正常方式对它们求和相同。

答案 5 :(得分:5)

操作的通常实现是“翻转位并添加1”,但是还有另一种定义它的方法可能使基本原理更清晰。 2的补码是你采用通常的无符号表示形式得到的形式,其中每个位控制下一个2的幂,并且只使最重要的项为负。

取一个8位值a 7 a 6 a 5 a 4 a 3 a 2 a 1 a 0

通常的无符号二进制解释是:
2 7 * a 7 + 2 6 * a 6 + 2 5 * a 5 + 2 4 * a 4 + 2 3 * a 3 + 2 2 * a 2 + 2 1 * a 1 + 2 0 *一个<子> 0
11111111 = 128 + 64 + 32 + 16 + 8 + 4 + 2 + 1 = 255

二者的补充解释是:
-2 7 * a 7 + 2 6 * a 6 + 2 5 * a 5 + 2 4 * a 4 + 2 3 * a 3 + 2 2 * a 2 + 2 1 * a 1 + 2 0 *一个<子> 0
11111111 = -128 + 64 + 32 + 16 + 8 + 4 + 2 + 1 = -1

其他任何一个位都没有改变含义,并且进入 7 是“溢出”并且预计不起作用,所以几乎所有的算术运算都在不加修改地工作(正如其他人所做的那样)说明)。符号幅度通常检查符号位并使用不同的逻辑。

答案 6 :(得分:4)

扩展其他答案:

以二进制补充

  • 添加与普通正整数添加的机制相同。
  • 减法也不会改变
  • 乘法!

分部确实需要不同的机制。

所有这些都是正确的,因为两个补码只是正常的模运算,我们选择通过减去模数来看一些数字为负数。

答案 7 :(得分:3)

二进制补码允许将负数和正数加在一起而不需要任何特殊逻辑。

如果您尝试使用您的方法添加1和-1  10000001(-1)
+00000001(1)
你得到了  10000010(-2)

相反,通过使用二进制补码,我们可以添加

11111111(-1)
+00000001(1) 你得到了  00000000(0)

减法也是如此。

此外,如果你试图从6减去4(两个正数)你可以2的补数4并将两个加在一起6 +( - 4)= 6 - 4 = 2

这意味着减去和添加正数和负数都可以通过cpu中的相同电路完成。

答案 8 :(得分:2)

阅读这个问题的答案,我发现了这个评论[编辑]。

  

2的补充0100(4)将是1100.如果我正常说,现在1100是12。所以,   当我说正常1100然后它是12,但是当我说2补充1100然后   它是-4?此外,在Java中存储1100(现在假设为4位)然后存储   如何确定它是+12还是-4? - hagrawal 7月2日16:53

在我看来,这篇评论中提到的问题非常有趣,所以我首先要重新改写它,然后提供一个答案和一个例子。

问题 - 系统如何确定如何解释一个或多个相邻字节?特别是,系统如何确定给定的字节序列是纯二进制数还是2的补数?

ANSWER - 系统建立如何通过类型解释字节序列。 类型定义

  • 必须考虑多少字节
  • 必须如何解释这些字节

示例 - 下面我们假设

  • char&#39; s长1个字节
  • short&#39; s长2个字节
  • int&{39}和float&#39; s长4个字节

请注意,这些尺寸特定于我的系统。虽然很常见,但它们可能因系统而异。如果您对系统中的内容感到好奇,请使用sizeof operator

首先,我们定义一个包含4个字节的数组,并将它们全部初始化为二进制数10111101,对应于十六进制数BD

// BD(hexadecimal) = 10111101 (binary)
unsigned char   l_Just4Bytes[ 4 ]   =   { 0xBD, 0xBD, 0xBD, 0xBD };

然后我们使用不同的类型读取数组内容。

unsigned charsigned char

// 10111101 as a PLAIN BINARY number equals 189
printf( "l_Just4Bytes as unsigned char  -> %hi\n", *( ( unsigned char* )l_Just4Bytes ) );

// 10111101 as a 2'S COMPLEMENT number equals -67
printf( "l_Just4Bytes as signed char    -> %i\n", *( ( signed char* )l_Just4Bytes ) );

unsigned shortshort

// 1011110110111101 as a PLAIN BINARY number equals 48573
printf( "l_Just4Bytes as unsigned short -> %hu\n", *( ( unsigned short* )l_Just4Bytes ) );

// 1011110110111101 as a 2'S COMPLEMENT number equals -16963
printf( "l_Just4Bytes as short          -> %hi\n", *( ( short* )l_Just4Bytes ) );

unsigned intintfloat

// 10111101101111011011110110111101 as a PLAIN BINARY number equals 3183328701
printf( "l_Just4Bytes as unsigned int   -> %u\n", *( ( unsigned int* )l_Just4Bytes ) );

// 10111101101111011011110110111101 as a 2'S COMPLEMENT number equals -1111638595
printf( "l_Just4Bytes as int            -> %i\n", *( ( int* )l_Just4Bytes ) );

// 10111101101111011011110110111101 as a IEEE 754 SINGLE-PRECISION number equals -0.092647
printf( "l_Just4Bytes as float          -> %f\n", *( ( float* )l_Just4Bytes ) );

RAM中的4个字节(l_Just4Bytes[ 0..3 ])始终保持完全相同。唯一改变的是我们如何解释它们。

同样,我们告诉系统 如何通过类型解释它们。

例如,上面我们使用以下类型来解释l_Just4Bytes数组

的内容
  • unsigned char:普通二进制文件中的1个字节
  • signed char:2&lt;补码中的1个字节
  • unsigned short:普通二进制表示法中的2个字节
  • short:2&lt;补码中的2个字节
  • unsigned int:普通二进制表示法中的4个字节
  • int:2&#39补;
  • 中的4个字节
  • float:IEEE 754单精度表示法中的4个字节

[编辑]此帖子已在用户4581301发表评论后进行了编辑。感谢您抽出宝贵的时间放弃那些有用的帮助!

答案 9 :(得分:1)

您可以观看斯坦福大学的Jerry Cain教授在第二讲(关于2的补充的解释从13:00开始的解释),在Standford的YouTube频道观看的一系列名为编程范例的讲座中。这是讲座系列的链接:http://www.youtube.com/view_play_list?p=9D558D49CA734A02

答案 10 :(得分:0)

我们只对加法和减法执行加法运算。我们将第二个操作数添加到第一个操作数中以进行添加。对于减法,我们将第二个操作数的2的补码添加到第一个操作数。

使用2的补码表示,我们不需要单独的数字组件用于仅减法加法器和补充器。

答案 11 :(得分:0)

值得注意的是,在一些早期的添加机器上,在数字计算机时代之前,通过让操作员在每个键上使用不同颜色的图例集输入值来执行减法(因此每个键将输入9减去要减去的数字),按一个特殊按钮会假定进入计算。因此,在一个六位数的机器上,要从一个值中减去1234,操作员将按下通常指示“998,765”的按键,然后按一个按钮将该值加1加到正在进行的计算中。二进制补码算术只是早期“十进制补码”算术的二进制等价物。

答案 12 :(得分:0)

通过补码方法进行减法的优点是减少了硬件 复杂性。不需要不同的数字电路进行加法和减法  加法和减法仅由加法器执行。

答案 13 :(得分:0)

好吧,你的意图并不是要反转二进制数的所有位。它实际上是从1中减去每个数字。这只是一个幸运的巧合,从1减去1得到0并从1减去0导致1.所以翻转位实际上是执行这个减法。

但为什么你发现每个数字与1的差异?好吧,你不是。您的实际意图是计算给定二进制数与另一个二进制数的差值,该二进制数具有相同的位数但仅包含1。例如,如果您的数字是10110001,当您翻转所有这些位时,您就会有效地计算(11111111 - 10110001)。

这解释了计算Two's Complement的第一步。现在让我们包括第二步 - 添加1 - 也在图片中。

将1添加到上述二进制方程式中:

11111111 - 10110001 + 1

你得到了什么?这样:

100000000 - 10110001

这是最后的等式。通过执行这两个步骤,你试图找到这个,最后的差异:二进制数从另一个二进制数中减去一个额外的数字并且包含零,除了在最具意义的位位置。

但为什么我们真的在这个差异之后呢?好吧,从现在开始,我想如果你阅读Wikipedia article会更好。

答案 14 :(得分:0)

使用二进制补码,因为它在电路中实现起来更简单,也不允许负零。

如果有x位,则两个补码的范围为+(2 ^ x / 2 + 1)到 - (2 ^ x / 2)。一个补码将从+(2 ^ x / 2)运行到 - (2 ^ x / 2),但允许负零(0000在4位1的补码系统中等于1000)。

答案 15 :(得分:0)

此处尚未提及的二进制补码表示的一个主要优点是二进制补码和,差值或乘积的低位仅依赖于 对应的位操作数。 -1的8位有符号值为11111111的原因是从最低8位为{{{0}的任何其他整数中减去任何整数,其最低8位为00000001。 1}}将产生一个最低8位为0000000的整数。在数学上,值-1将是1的无限字符串,但是特定整数类型范围内的所有值将全部为1或全部0超过某个点,因此计算机可以“签名扩展”数字的最高有效位,好像它代表无穷多的1或0。

二进制补码只是在处理大于二进制机器的自然字大小的类型时唯一有效的有符号数表示,因为在执行加法或减法时,代码可以获取每个操作数的最低块,计算结果的最低块,然后存储,然后加载每个操作数的下一个块,计算结果的下一个块,然后存储,等等。因此,即使是需要所有加法和减法的处理器也要经过一个8 -bit寄存器可以合理有效地处理32位有符号数(当然,比32位寄存器慢,但仍然可行)。

当使用C标准允许的任何其他有符号表示时,结果的每个位都可能受到操作数的任何位的影响,因此必须立即在寄存器中保存整个值,否则需要遵循计算有一个额外的步骤,至少在某些情况下,需要读取,修改和重写结果的每个块。

答案 16 :(得分:0)

有不同类型的表示形式,

  1. 无符号数字表示
  2. 带符号的数字表示
  3. 一个人的补语表示
  4. 二进制补码表示法

-无符号数字表示,仅用于表示正数

-带符号的数字表示形式,用于表示正数和负数。在带符号的数字表示中,MSB位表示符号位,其余的位表示数字。当MSB为0时表示数字为正,当MSB为1时表示数字为负。

带符号数字表示的问题是0有两个值。

补码表示法的问题是0有两个值。

但是如果我们使用二进制补码表示,那么0只会有一个值,这就是为什么我们用二进制补码表示负数的原因。

来源:Why negative numbers are stored in two's complement form bytesofgigabytes

答案 17 :(得分:-1)

为什么Two2's Complement用于表示负数而不是One's Complement系统的一个令人满意的答案是                         Two's Complement系统解决了多个表示0 的问题以及需要 end-around-carry ,它存在于表示负数的One's补充系统中。

有关详细信息,请访问https://en.wikipedia.org/wiki/Signed_number_representations

进行端到端访问 https://en.wikipedia.org/wiki/End-around_carry

答案 18 :(得分:-1)

因为CPU制造商很懒!