为什么更喜欢两个补码超过符号和数量签名的数字?
我只是好奇,如果有一个原因,为了在二进制表示-1,二进制补码使用:翻转的位和加1?
-1表示为11111111(二进制补码),而不是(对我来说比较直观)10000001是二进制1,第一位是负数标志。
免责声明:我的工作不依赖二进制算术!
这样做,添加不需要有任何处理负数的特殊逻辑。 看看维基百科上的文章 。
假设你有两个数字,2和-1。 用你的“直觉”表示数字的方法,分别是0010
和1001
(我坚持4位大小)。 这两个补码的方式是0010
和1111
。 现在,假设我想添加它们。
补码的加法非常简单。 您通常添加数字,并在最后的任何进位被丢弃。 所以他们添加如下:
0010 + 1111 =10001 = 0001 (discard the carry)
0001
是1,这是“2 +( – 1)”的预期结果。
但在你的“直觉”方法中,添加更复杂:
0010 + 1001 = 1011
这是-3,对不对? 在这种情况下,简单的添加不起作用。 你需要注意的是,其中一个数字是负数,如果是这样的话,使用不同的algorithm。
对于这种“直观的”存储方法,减法与添加操作不同,在添加之前需要对数字进行额外的检查。 由于您希望最基本的操作(加法,减法等)尽可能快,所以您需要以可以使用最简单的algorithm的方式存储数字。
另外,在“直观的”存储方法中,有两个零:
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位。 这是每个应用程序中最基本和最常见的操作之一。
维基百科全说:
二进制补码系统的优点是不需要加法和减法电路检查操作数的符号来决定是加还是减。 这个属性使得系统的实现更简单,并且能够容易地处理更高精度的算术。 此外,零只有一个表示forms,消除了在补零系统中存在的与零负相关的微妙之处。
换句话说,加法是一样的,不pipe数字是否定的。
二的补码允许加法和减法以正常的方式完成(就像你缠绕无符号数字)。 它还可以防止-0(用一种单独的方式来表示0,用比较数字的正常比特方法不等于0)。
即使这个问题很老,让我把我的2美分。
在我解释这个之前,让我们回到基础。 2'补码是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'bit数字+它的2的补码= 0这意味着数字的二进制补码= – (该数字的)
所有这一切都产生了一个问题,为什么我们只能使用n位的(n-1)来表示正数,为什么最左边的第n位表示符号(最左边的位表示+ ve数,1表示符号-ve号码)。 例如,为什么我们只使用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)系统仍然有效。 这里唯一不明确的地方是12的二进制补码是0100,它在二进制补码系统中除了代表-12之外,还含义地表示+8。
如果正数在其最左边总是有0,这个问题将被解决。 在这种情况下,它们的2的补码在它们的最左边总是有一个1,而且我们不会有相同的一组比特表示一个2的补数和一个+ ve数的模糊性。
这是为了简化数字的总和和差异。 在2的补码中编号为负数和正数的总和与以正常方式总结它们相同。
该操作的通常实现是“翻转位并加1”,但还有另一种定义它的方式,可能使理由更清楚。 2的补码是你得到的forms,如果你采取通常的无符号表示,其中每个位控制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 * a 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 * a 0
11111111 = -128 + 64 + 32 + 16 + 8 + 4 + 2 + 1 = -1
其他所有的位都没有任何意义,而进入7就是“溢出”,并且不会有效,所以几乎所有的算术运算都没有改变(正如其他人所指出的那样)。 符号大小通常检查符号位并使用不同的逻辑。
为了扩大他人的答案:
在二补
- 添加和普通的正整数相同。
- 减法也不会改变
- 倍增!
该部门确实需要不同的机制。
所有这些都是真实的,因为二进制补码只是普通的模运算,我们select通过减去模来看一些数字为负数。
二进制补码允许将负数和正数加在一起,而不需要任何特殊的逻辑。
如果您尝试使用您的方法添加1和-1
10000001(-1)
+00000001(1)
你得到
10000010(-2)
相反,通过使用二进制补码,我们可以添加
11111111(-1)
+00000001(1)你得到了
00000000(0)
减法也是如此。
另外,如果你试图从6中减去4(两个正数),你可以补2,并把它们加在一起6 +(-4)= 6 – 4 = 2
这意味着正负号的减法和相加都可以通过cpu中的同一个电路完成。
读这个问题的答案,我碰到了这个评论[编辑]。
0100(4)的2的补码是1100.现在如果我正常说的话,1100是12。 所以,当我说正常1100,那么它是12,但是当我说2的补码1100,那么它是-4? 另外,在1100(假设现在假定是4位)的Java中,如何确定它是+12还是-4? – 哈格瓦尔7月2日16:53
在我看来,这个评论中提出的问题是相当有趣的,所以我首先要重新修改它,然后提供一个答案和一个例子。
问题 – 系统如何确定如何解释一个或多个相邻字节? 特别是,系统如何确定一个给定的字节序列是一个普通的二进制数还是一个2的补码数?
解答 – 系统build立如何通过types来解释字节序列。 types定义
- 必须考虑多less个字节
- 如何解释这些字节
示例 – 下面我们假设
-
char
的长度是1个字节 -
short
的是2个字节长 -
int
和s的长度是4个字节
请注意,这些尺寸是特定于我的系统。 虽然相当普遍,但可能会因系统而异。 如果您对系统上的内容感到好奇,请使用sizeof运算符 。
首先我们定义一个包含4个字节的数组,并将其全部初始化为二进制数字10111101
,对应于hex数字BD
。
// BD(hexadecimal) = 10111101 (binary) unsigned char l_Just4Bytes[ 4 ] = { 0xBD, 0xBD, 0xBD, 0xBD };
然后我们使用不同的types读取数组内容。
unsigned char
和有signed 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 short
和short
// 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 int
, int
和float
// 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 ]
)始终保持完全相同。 唯一改变的是我们如何解读它们。
我们再次告诉系统如何通过types来解释它们。
例如,上面我们使用了以下types来解释l_Just4Bytes
数组的内容
-
unsigned char
:1个二进制字节 -
signed char
:2个补码中的1个字节 -
unsigned short
:2个字节的纯二进制表示法 -
short
2个字节是2的补码 -
unsigned int
:4个字节的纯二进制表示法 -
int
:2的补码中的4个字节 -
float
:IEEE 754单精度表示法中的4个字节
[编辑]这篇文章已被user4581301评论后编辑。 感谢您花时间放下这些有用的线条!
斯坦福大学的杰里·凯因(Jerry Cain)教授在斯坦福大学YouTube频道的一系列讲座“Programming Paradigms”中进行了第二讲(关于2的补充开始于13:00左右)。 以下是讲座系列的链接: http : //www.youtube.com/view_play_list ?p = 9D558D49CA734A02 。
使用二进制补码是因为它在电路中实现起来更简单,并且不允许负零。
如果有x位,则二进制补码的范围将从+(2 ^ x / 2 + 1)到 – (2 ^ x / 2)。 补码将从+(2 ^ x / 2)运行到 – (2 ^ x / 2),但允许一个负零(在4位1的补码系统中,0000等于1000)。
那么,你的意图是不是真的要扭转你的二进制数的所有位。 实际上是从1中减去每个数字。只是一个幸运的巧合,从1减去1得到0,从1减去0得到1.所以翻转位有效地进行这种减法。
但为什么你find每个数字与1的差异? 那么,你不是。 您的实际意图是计算给定的二进制数与另一个具有相同数字位数但只包含1的二进制数的差异。 例如,如果您的电话号码是10110001,那么当您翻转所有这些位时,就可以有效地计算(11111111 – 10110001)。
这解释了计算Two's Complement的第一步。 现在让我们包括第二步 – 在图片中加1。
加1到上面的二进制方程:
11111111 – 10110001 + 1
你得到了什么? 这个:
100000000 – 10110001
这是最后的等式。 通过执行这两个步骤,你试图find这个,最后的区别:二进制数字从另一个二进制数字减去一个额外的数字,并包含零,除了最有意义的位置。
但是为什么我们在这个区别之后呢还是渴望呢? 那么,从这里开始,我想如果你阅读维基百科的文章会更好。
我们只进行加法和减法的加法运算。 我们将第二个操作数添加到第一个操作数中。 对于减法,我们将第二个操作数的2的补码加到第一个操作数上。
用2的补码表示,我们不需要单独的数字分量进行减法,只使用加法器和补码器。
值得注意的是,在一些早期的加法计算机上,在数字计算机的日子之前,减法将通过让操作员使用每个键上的不同颜色的图例集来input值来实现(所以每个键将input9减去减去),然后按一个特殊的button就会假定进位进行计算。 因此,在一个六位数字的机器上,为了从一个值中减去1234,操作员将击中通常表示“998,765”的键,并按下一个button以将该值加1,以便进行计算。 二进制补码运算就是早期的“十进制补码”运算的二进制等值。
通过补码方法执行减法的优点是减less了硬件
复杂度高。不需要用不同的数字电路进行加减运算。加法和减法都是由加法器来完成的。
这里还没有提到的二进制补码表示法的一个主要优点是二进制补码和,差或乘积的低位只取决于操作数的相应位。 -1的8位有符号值为11111111
是从最低8位为0000000
任何其他整数中减去最低8位为00000001
的任何整数将产生最低8位为11111111
的整数。 在math上,值-1是1的无限长string,但是在特定整数types范围内的所有值都将是全1或全零,因此计算机可以方便地“签名扩展”一个数字的最重要的位,就好像它代表无限的1或0。
二进制补码是处理大于二进制机器的自然字大小的types的唯一有符号数表示,因为执行加法或减法时,代码可以获取每个操作数的最低块,计算最低块结果,并存储它,然后加载每个操作数的下一个块,计算结果的下一个块,并存储该等等。因此,即使是需要所有加法和减法的处理器通过单个8位寄存器可以合理有效地处理32位有符号数(比使用32位寄存器慢,当然,但仍然可行)。
当使用C标准允许的任何其他符号表示时,结果的每一位都可能受到操作数的任何位的影响,因此有必要一次保存寄存器中的整个值,或者使用额外的计算至less在某些情况下,需要读取,修改和重写每个结果块。
一个令人满意的答案是为什么Two2的补数用来表示负数而不是One的补码系统,Two补码系统解决了0的多重表示问题,并且在补码表示系统中存在的end-around-carry问题数字。
有关更多信息,请访问https://en.wikipedia.org/wiki/Signed_number_representations