我只是好奇,为了在二进制中表示-1,是否有一个原因,使用了2的补码:翻转位并加1?
-1由11111111(2的补码)表示,而不是(对我来说更直观)10000001,10000001是二进制1,第一位为负标志。
免责声明:我的工作不依赖二进制算术!
我只是好奇,为了在二进制中表示-1,是否有一个原因,使用了2的补码:翻转位并加1?
-1由11111111(2的补码)表示,而不是(对我来说更直观)10000001,10000001是二进制1,第一位为负标志。
免责声明:我的工作不依赖二进制算术!
当前回答
尽管这个问题已经很老了,但还是让我说说我的看法。
在我解释这个之前,让我们回到基础上来。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 000,即0(因为我们处理的是4位数字,(左边的1是溢出)
So ,
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个数字)。例如,为什么我们在Java中只使用int的前31位来表示正数,如果第32位是1,它是-ve数。
1100 (lets assume 12 in 4 bit system)
+0100(2's complement of 12)
___________________________
1 0000(结果为0,进位1溢出)
因此(n + 2'补n) = 0的方程组仍然成立。这里唯一的歧义是2对12的补码是0100,它也模糊地表示+8,而不是在2s补码系统中表示-12。
如果正数的最左边总是有一个0,这个问题就可以解决了。在这种情况下,它们的2的补位总是在最左边有一个1,我们就不会有相同的位集表示2的补位数和+ve数的模糊性。
其他回答
2的补数允许以正常的方式进行加减法(就像对无符号数字进行绕线运算一样)。它还防止了-0(一种单独的表示0的方法,如果使用常规的逐位比较数字的方法,它将不等于0)。
在阅读这个问题的答案时,我看到了这条评论[编辑]。
0100(4)的补数是1100。现在1100是12如果我说正常的话。所以, 当我说标准1100时,它是12,但当我说2的补足1100时,那么 是-4?另外,在Java中,当1100(假设现在是4位)被存储时 如何确定它是+12还是-4 ?——hagrawal 7月2日16:53
在我看来,这条评论中提出的问题非常有趣,所以我想首先重新措辞,然后提供一个答案和一个例子。
系统如何确定一个或多个相邻字节必须如何解释?特别是,系统如何确定给定的字节序列是普通二进制数还是2的补数?
ANSWER -系统建立如何通过类型解释字节序列。 类型定义
需要考虑多少字节 如何解释这些字节
示例-下面我们假设
Char的长度为1字节 短的是2字节长 Int型和float型的长度是4字节
请注意,这些大小是特定于我的系统的。尽管非常常见,但它们可能因系统而异。如果您想知道它们在您的系统中是什么,请使用sizeof操作符。
首先,我们定义一个包含4个字节的数组,并将它们初始化为二进制数10111101,对应于十六进制数BD。
// BD(hexadecimal) = 10111101 (binary)
unsigned char l_Just4Bytes[ 4 ] = { 0xBD, 0xBD, 0xBD, 0xBD };
然后我们使用不同的类型读取数组内容。
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 ) );
未签名的空头和空头
// 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 ) );
内存中的4个字节(l_Just4Bytes[0..]3)始终保持完全相同。唯一改变的是我们如何理解它们。
同样,我们告诉系统如何通过类型来解释它们。
例如,上面我们使用了以下类型来解释l_Just4Bytes数组的内容
无符号字符:纯二进制的1字节 有符号字符:2的补码中的1字节 无符号短:2字节的纯二进制表示法 短:2字节的2补码 Unsigned int: 4字节的纯二进制表示法 Int: 4字节的2的补码 float:在IEEE 754单精度表示法中为4字节
[编辑]这篇文章是在user4581301的评论后编辑的。谢谢你抽出时间来写这些有用的句子!
为什么用Two2的补语系统来表示负数,而不是用One的补语系统,一个令人满意的答案是 二的补语系统解决了一的补语系统中存在的表示负数的0的多重表示和对进位的需要。
欲了解更多信息,请访问https://en.wikipedia.org/wiki/Signed_number_representations
用于末端绕行访问 https://en.wikipedia.org/wiki/End-around_carry
2的补语允许负数和正数相加,而不需要任何特殊的逻辑。
如果你想用你的方法做1和-1相加 10000001 (1) + 00000001 (1) 你得到 10000010 (2)
相反,通过使用2的补数,我们可以相加
11111111 (1) + 00000001 (1) 你得到 00000000 (0)
减法也是如此。
同样,如果你试着用6减去4(两个正数),你可以用2补4,然后把两者相加6 + (-4)= 6 -4 = 2
这意味着正数和负数的减法和加法都可以由cpu中的同一个电路完成。
这样加法就不需要任何特殊的逻辑来处理负数了。在维基百科上查看这篇文章。
假设有两个数,2和-1。在表示数字的“直观”方式中,它们将分别为0010和1001(我坚持使用4位的大小)。两者互为补足,分别是0010和1111。现在,假设我想把它们相加。
2的补语加法非常简单。你通常加数字,任何进位在最后被丢弃。所以它们相加如下:
0010
+ 1111
=10001
= 0001 (discard the carry)
0001是1,这是“2+(-1)”的预期结果。
但在你的“直观”方法中,添加更复杂:
0010
+ 1001
= 1011
等于-3,对吧?简单的加法在这种情况下行不通。你需要注意,其中一个数字是负的,如果是这种情况,就使用不同的算法。
对于这种“直观的”存储方法,减法是一种不同于加法的操作,在加法之前需要对数字进行额外的检查。由于您希望最基本的操作(加法、减法等)尽可能快,因此需要以允许您使用尽可能简单的算法的方式存储数字。
此外,在“直观”存储方法中,有两个0:
0000 "zero"
1000 "negative zero"
它们直观上是相同的数字,但存储时有两个不同的值。每个应用程序都需要采取额外的步骤来确保非零值也不是负零。
以这种方式存储int型还有另一个好处,那就是当你需要扩展存储值的寄存器的宽度时。对于2的补数,在8位寄存器中存储一个4位数就是重复它的最高位:
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位。这是每个应用程序中最基本和最常见操作之一中的一个小小的额外步骤。