我只是很好奇是否有理由为什么要以二进制形式表示-1,使用二进制补码:翻转位并加1?
-1由11111111(二进制补码)表示,而不是(对我来说更直观)10000001,后者是二进制1,第一位为负标志。
免责声明:我的工作不依赖于二进制算法!
我只是很好奇是否有理由为什么要以二进制形式表示-1,使用二进制补码:翻转位并加1?
-1由11111111(二进制补码)表示,而不是(对我来说更直观)10000001,后者是二进制1,第一位为负标志。
免责声明:我的工作不依赖于二进制算法!
Answers:
这样做是为了使加法处理负数不需要任何特殊的逻辑。查看Wikipedia上的文章。
假设您有两个数字2和-1。用“直观”表示数字的方式,它们分别是0010
和1001
(我坚持使用4位的大小)。以两者的互补方式,它们是0010
和1111
。现在,假设我要添加它们。
二进制补码非常简单。您通常会添加数字,并且末尾的任何进位都将被丢弃。因此,它们添加如下:
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位。这是每个应用程序中存在的最基本,最常见的操作之一,这是额外的微小步骤。
how we arrived at 2s compliment the first place.
cs.cornell.edu/~tomf/notes/cps104/twoscomp.html
1
表示-8
,其余三个1
小号表明4
,2
和1
分别,所以-8+4+2+1 = -1
。
维基百科说了一切:
二进制补码系统的优点是不需要加法和减法电路检查操作数的符号以确定是加还是减。此属性使系统不仅易于实现,而且能够轻松处理高精度算术。而且,零只有一个表示,从而消除了与负零相关的微妙之处,后者存在于补码系统中。
换句话说,无论数字是否为负,加法都相同。
即使这个问题老了,让我投入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号)。例如,如果第32位为1,则为什么仅使用java中int的前31位表示正数,即它的a -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数的同一位集合的歧义。
这是为了简化数字的和与差。在2的补码中编码的负数和正数之和与以常规方式将它们求和相同。
该操作的通常实现是“翻转位并加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 * 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则是“溢出”且不希望执行,因此几乎所有算术运算都无需修改即可工作(正如其他人所指出的那样)。符号幅度通常检查符号位并使用不同的逻辑。
二进制补码允许将负数和正数加在一起,而无需任何特殊逻辑。
如果您尝试使用方法
10000001(-1)
+00000001(1)加1和-1,
则会得到
10000010(-2)
相反,通过使用二进制补码,我们可以添加
11111111(-1)
+00000001(1)得到
00000000(0)
减法也是如此。
另外,如果尝试从6(两个正数)中减去4,则可以2的补数4并将两个数相加6 +(-4)= 6-4 = 2
这意味着正负数的减法和加法都可以通过cpu中的同一电路完成。
扩展其他答案:
互为补充
划分确实需要不同的机制。
所有这些都是正确的,因为二进制补码只是常规的模运算,我们选择通过减去模来将某些数视为负数。
unsigned mul(unsigned short x, unsigned short y) { return x*y; }
[16位short; 32位int]偶尔会产生的代码,将故障如果该产品是比较大2147483647
阅读该问题的答案,我发现了此评论[编辑]。
2的0100(4)的补码将是1100。如果我正常地说,现在1100是12。因此,当我说正常1100时,它是12,但是当我说2的补码1100时,它是-4?同样,在Java中,当存储1100(现在假设为4位)时,如何确定它是+12还是-4 ??– hagrawal 7月2日16:53
我认为,此评论中提出的问题非常有趣,因此我想首先重新表述一下,然后提供答案和示例。
问题–系统如何确定必须如何解释一个或多个相邻字节?特别是,系统如何确定给定的字节序列是纯二进制数还是2的补码?
答案–系统确定如何通过类型解释字节序列。类型定义
示例–下面我们假设
char
的长度为1个字节short
的长度为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 ) );
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(l_Just4Bytes[ 0..3 ]
)中的4个字节始终保持完全相同。唯一改变的是我们如何解释它们。
同样,我们告诉系统如何通过type解释它们。
例如,上面我们使用以下类型来解释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发表评论后已被编辑。感谢您抽出宝贵的时间写这几行有用的内容!
int x = -4
,那么该printf("%d" , x)
如何解释呢?还有unsigned int
and和signed int
and %d
和%u
... 之间的区别是...这已经困扰了我很长时间了。谢谢。
int
类型时,signed
修饰符为默认值。这意味着int
和signed int
是完全相同的类型。因此,这两个定义int i = -4;
和signed int i = -4;
具有相同的含义。
您可以观看斯坦福大学的杰里·凯恩(Jerry Cain)教授在第二堂课(关于2的补码的解释大约在13:00左右开始)中讲解两者的补码,这一系列讲座可以从Standford的YouTube频道观看,并可以观看。这是讲座系列的链接:http : //www.youtube.com/view_play_list ? p= 9D558D49CA734A02。
好吧,您的意图并不是真正地反转二进制数字的所有位。实际上是从1减去其每个数字。这只是一个幸运的巧合,从1减去1得出0,从1减去0得到1。所以翻转位有效地实现了这种减法。
但是,为什么要找出每个数字与1的差呢?好吧,你不是。您的实际目的是计算给定的二进制数与另一个具有相同位数但只包含1的二进制数的差。例如,如果您的数字是10110001,那么当您翻转所有这些位时,您实际上在进行计算(11111111-10110001)。
这说明了计算Two's Complement的第一步。现在,让我们在图片中包括第二步-加1。
将1加到上述二进制方程式中:
11111111-10110001 +1
你得到了什么?这个:
100000000-10110001
这是最终方程式。通过执行这两个步骤,您将找到最终的区别:从另一个二进制数减去一个二进制数后再减去一个二进制数,该二进制数具有一个额外的数字,并且除了最高有效位位置之外,还包含零。
但是,为什么我们真的渴望这种差异呢?好吧,从现在开始,我想如果您阅读Wikipedia文章会更好。
此处尚未提及的二进制补码表示法的主要优点是,二进制补码的和,差或乘积的低位仅取决于操作数的相应位。其原因,对于8位有符号值-1是11111111
是减去任何其最低8位是整数00000001
从任何其他整数,其最低8位是0000000
将产生一个整数,其最低8位是11111111
。从数学上来说,值-1是一个无限的1字符串,但是在特定整数类型范围内的所有值都将是某个点之后的全1或全0,因此对于计算机“符号扩展”数字的最高有效位,就好像它代表1或0的无限个数一样。
二进制补码几乎是唯一一种在处理大于二进制机器自然字长的类型时能很好工作的带符号数表示形式,因为在执行加法或减法时,代码可以获取每个操作数的最低块,计算出结果,并将其存储,然后加载每个操作数的下一个块,计算结果的下一个块,并将其存储,依此类推。因此,即使是需要所有加法和减法都要通过单个8位寄存器的处理器可以合理有效地处理32位带符号数字(当然比32位寄存器要慢,但仍然可行)。
当使用C标准允许的任何其他带符号的表示形式时,结果的每一位都可能受到操作数的任何位的影响,因此有必要一次将整个值保存在寄存器中,否则必须进行额外的计算至少在某些情况下,需要读取,修改和重写结果的每个块的步骤。
有不同类型的表示形式,分别是:
-无符号数字表示,仅用于表示正数
-带符号的数字表示形式,用于表示正数和负数。在带符号的数字表示中,MSB位表示符号位,其余的位表示数字。当MSB为0时,数字为正;当MSB为1时,数字为负。
有符号数字表示的问题是0有两个值。
一个补码表示的问题是0有两个值。
但是,如果我们使用二进制补码表示,那么0只会有一个值,这就是为什么我们用二进制补码表示负数的原因。
资料来源:为什么负数以二进制补码形式存储
对于为什么使用Two2的补码而不是One的补码系统来表示负数的一个令人满意的答案是,Two的补码系统解决了在代表负数的One的补码系统中存在的0的多重表示以及端到位进位的问题。数字。
有关更多信息,请访问https://en.wikipedia.org/wiki/Signed_number_representations
对于“随身携带”,请访问 https://en.wikipedia.org/wiki/End-around_carry