我学的是计算机系统课程,部分内容是苦苦挣扎的。我想了解它,但我读过的所有东西都没有把它放在一起。我读过维基百科的文章和其他各种文章,包括我的课本。
因此,我想开始这篇社区维基文章来定义Two's Complement是什么,如何使用它,以及它如何在强制转换(从有符号到无符号,反之亦然)、逐位操作和位移位操作等操作中影响数字。
我所希望的是一个程序员很容易理解的一个清晰简洁的定义。
二进制补码是一种存储整数的聪明方法,因此常见的数学问题非常容易实现。
为了理解,你必须考虑二进制中的数字。
它基本上是说,
让我们尝试使用一个4位的迷你字节(我们将其称为一点一点地咬 - 1/2字节)。
0000
0001
0010
0011
0100
0111
这是我们目前能找到的阳性结果。2__abc0-1 = 7。
负面影响:
1111
1110
1101
1100
1000
请注意,负数会得到一个额外的值(1000 = -8),而正数则没有。这是因为0000用于零。这可以被认为是计算机的数轴。
区分正数和负数
这样做,第一个比特得到了“签名”的作用;位,因为它可用于区分非负十进制值和负十进制值。如果最高有效位是1,则二进制值可以说是负数,如果最高有效位(最左边)是0,则可以说十进制值是非负数。
1
0
“Sign-magnitude"负数只是将其正数对应数的符号位翻转,但这种方法必须处理将1000(一个1后面跟着所有__abc2)解释为“负零”;这很令人困惑。
“的”complement"负数只是正数的位补,这也导致了令人困惑的“负零”。1111(所有的1)。
除非你的工作非常接近硬件,否则你可能不需要处理个位补或符号幅度整数表示。
我想知道是否有比维基百科上的文章更好的解释。
你试图用2的补表示法解决的基本问题是存储负整数的问题。
首先,考虑一个存储在4位的无符号整数。您可以拥有以下内容
0000 = 0 0001 = 1 0010 = 2 ... 1111 = 15
这些是无符号的,因为没有指示它们是负的还是正的。
要存储负数,您可以尝试一些方法。首先,您可以使用符号幅度表示法,它将第一个位指定为符号位来表示+/-,其余位表示幅度。还是用4位假设1代表- 0代表+那么你就有
0000 = +0 0001 = +1 0010 = +2 ... 1000 = -0 1001 = -1 1111 = -7
所以,你看到问题了吗?我们有正0和负0。更大的问题是二进制数的加减法。使用符号幅度进行加减法的电路将非常复杂。
是什么
0010 1001 + ----
?
另一个系统是多余的符号。你可以存储负数,你可以摆脱两个0的问题但加减法仍然很困难。
于是就有了二的补。现在您可以存储正整数和负整数,并相对轻松地执行算术。有许多方法可以将一个数转换为二的补数。这是一个。
将数字转换为二进制(暂时忽略符号) 例如,5是0101,-5是0101
如果这个数字是正数,那么你就完成了。 例如,5是二进制的0101,使用二补表示法
如果数字是负的,那么
3.1找到补体(逆0和1) 例如,-5是0101,所以找到补体是1010
使用2的补码,想想会有多简单。
2 = 0010 -3 = 1101 + ------------- -1 = 1111
将1111转换为十进制:
这个数从1开始,所以它是负的,所以我们找到1111的补,也就是0000。
将1加到0000,得到0001。
将0001转换为十进制,即1。
应用符号= -1。
大作。
我喜欢lavinio的回答,但变换部分增加了一些复杂性。通常情况下,可以选择在保留符号位的情况下移动位,或者不保留符号位。这是将数字处理为有符号数字(-8到7表示小块,-128到127表示字节)或全范围无符号数字(0到15表示小块,0到255表示字节)之间的选择。
想象一下,你有有限数量的比特/比特/数字等等。将0定义为所有数字都为0,并自然向上计数:
00 01 02 ..
最终你会溢出。
98 99 00
我们有两位数字,可以表示从0到100的所有数字。所有这些数字都是正数!假设我们也想表示负数?
我们真正拥有的是一个循环。2之前的数字是1。1之前的数字是0。0之前的数字是…99。
为了简单起见,我们设任何大于50的数都是负数。0 ~ 49代表0 ~ 49。“99”是-1,“98”是-2,…“50”是-50。
这个表示是十的补。计算机通常使用二进制补码,它是相同的,只是使用位而不是数字。
10的补数的好处在于加法只是工作。你不需要做任何特殊的加法和负数!
10101
01010
01010+1=01011
这是一种对负整数进行编码的聪明方法,该方法将数据类型中大约一半的位组合保留给负整数,并且将大多数负整数与其对应的正整数相加会导致进位溢出,使结果为二进制零。
因此,在2的补码中,如果1是0x0001,那么-1是0x1111,因为这将导致0x0000的组合和(溢出1)。
像我看到的大多数解释一样,上面的解释清楚如何使用2的补码,但并没有真正解释它们在数学上是。我会试着这么做,至少对整数来说是这样的,我会先介绍一些你们可能熟悉的背景知识。
回想一下十进制是如何工作的: 2345 是一种书写 2 ×103. + 3. ×102 + 4 ×101 + 5 ×10 __abc8。
以同样的方式,二进制是一种只使用0和1来写数字的方法,遵循相同的思路,但将上面的10替换为2。然后在二进制中, 1111是一种书写 1 ×23. + 1 ×22 + 1 ×21 + 1 ×210,如果你算出来,结果等于15(以10为底)。这是因为它是 8+4+2+1 = 15。
这对于正数来说很好。它甚至适用于负数,如果你愿意在负数前面加一个负号,就像人类对待小数一样。在某种程度上,这甚至可以在计算机上完成,但我从20世纪70年代初就没见过这样的计算机了。我将把原因留到另一个讨论。
对于计算机来说,使用补充表示负数更有效。这里有一些经常被忽视的东西。补表示法涉及到数字数字的某种反转,甚至是在正常正数之前隐含的零。这很尴尬,因为问题来了:所有这些?这可能是一个无限的数字要考虑。
幸运的是,计算机并不代表无穷。数字被限制在特定的长度(或者宽度,如果你喜欢)。所以让我们回到正二进制数,但有一个特定的大小。在这些例子中,我将使用8个数字(“位”)。所以我们的二进制数实际上是 00001111或 0 ×27 + 0 ×26 + 0 ×25 + 0 ×24 + 1 ×200 + 1 ×201 + 1 ×203 + 1 ×2 __abc15
为了形成2的补负,我们首先将所有(二进制)数字补全,形成 11110000,然后加上1,形成 11110001,但我们如何理解这意味着-15?
答案是,我们改变了高阶位(最左边的位)的含义。对于所有负数,该位将是1。这个变化将是改变它在数字中所占值的符号。所以现在我们的11110001被理解为代表 -1 ×27 + 1 ×26 + 1 ×25 + 1 ×24 + 111100010 ×2111100011 + 0 ×2111100012 + 111100010 ×2111100014 + 1 ×2111100016注意到表达式前面的“-”了吗?这意味着符号位的权重为-27,即-128(以10为基数)。所有其他位置保留无符号二进制数中相同的权值。
算出-15,结果是-128 + 64 + 32 + 16 + 1用计算器试试。它是-15。
在我所见过的计算机中表示负数的三种主要方式中,2的补码因为方便而在一般使用中胜出。不过,它有一个奇怪的地方。因为它是二进制的,所以必须有偶数个可能的位组合。每个正数都可以和负数配对,但只有一个零。负零等于零。所以还有一个组合,符号位是1的数字,其他地方都是0。对应的正数不符合所使用的比特数。
关于这个数字更奇怪的是,如果你试图通过互补和加1来形成正数,你会得到相同的负数。0会这样做似乎很自然,但这是出乎意料的,完全不是我们习惯的行为,因为除了计算机,我们通常认为数字是无限供应的,而不是这种固定长度的算术。
这只是怪胎的冰山一角。表面之下还有更多的东西在等待着,但这就足够我们讨论了。如果你研究定点算术中的“溢出”,你可能会发现更多。如果你真的想深入了解它,你可能还会研究“模算术”。
2的补码对于查找二进制值非常有用,但是我想到了一个更简洁的方法来解决这样的问题(从未见过其他人发布它):
以二进制为例:1101(假设空格“1”是符号)等于3。
使用2的补码,我们可以这样做…翻1101到0010…加上0001 + 0010 ===>得到0011。0011的正二进制= 3。因此1101 = 3!
我意识到: < / >强
而不是所有的翻转和加法,你可以只做一个基本的方法来解决正二进制(假设0101)是(23. * 0) + (22 * 1) + (21 * 0) + (20 * 1) = 5。
用否定句做同样的概念!(稍微扭曲一下)
以1101为例:
对于第一个数字,do -(23. * 1) = 8,而不是23. * 1 = 8。
然后像往常一样,执行8 + (22 * 1) + (21 * 0) + (20 * 1) = 3
为了得到12的恭维部分,我们只需将所有位反转,然后加1。 12的二进制反转是11110011。这也是逆码(一个人的补码)。 现在我们需要添加一个,现在是11110100.
所以11110100是12的赞美!这样想很简单。
现在你可以用二进制形式来解决上面的10 - 12问题了。
00001010 11110100 ----------------- 11111110
2的补语:当我们用一个数字的1的补语加一个额外的1时,我们将得到2的补语。例如:100101它的1的补码是011010和2的补码是011010+1 = 011011(通过添加1的补码)更多信息
从数学的角度来看这两个补体系统是有道理的。在ten的补语中,这个想法本质上是“隔离”差异。
示例:63 - 24 = x
我们把24的补数相加,也就是(100 - 24)实际上,我们要做的就是在方程两边加100。
现在方程是:100 + 63 - 24 = x + 100,这就是为什么我们要去掉100(或10或1000或其他)。
由于必须从一长串零中减去一个数字的不方便情况,我们使用“减基数补”系统,在十进制系统中,9的补。
当我们看到一串大的9减去一个数时,我们只需要把数字倒过来。
例如:99999 - 03275 = 96724
这就是为什么在9的补数之后加1。你可能从儿时的数学中知道,9通过“偷走”1变成了10。所以基本上就是10的补位差减去1。
在二进制中,2的补数等于10的补数,而1的补数等于9的补数。主要的区别在于,我们不是试图用10的幂来分离差异(将10、100等添加到等式中),而是试图用2的幂来分离差异。
正是因为这个原因,我们把比特位颠倒。就像小数中的被减数是一串9一样,二进制中的被减数也是一串1。
例如:111111 - 101001 = 010110
因为1链比2的幂小1,它们从差值中“偷”了1,就像小数点中的9一样。
当我们使用负二进制数时,我们实际上是在说
0000 - 0101 = x
1111 - 0101 = 1010
1111 + 0000 - 0101 = x + 1111
为了“分离”x,我们需要加1,因为1111离10000只有1,我们去掉前导的1,因为我们只是把它加到原始的差值上。
1111 + 1 + 0000 - 0101 = x + 1111 + 1
10000 + 0000 - 0101 = x + 10000
只要两边都去掉10000就得到x,这是基本的代数。
最简单的答案:
1111 + 1 =(1)0000。所以1111一定是-1。那么-1 + 1 = 0。
理解这些对我来说是完美的。
你也可以使用在线计算器来计算一个十进制数的补二表示:http://www.convertforfree.com/twos-complement-calculator/
到目前为止,许多答案都很好地解释了为什么2的补数被用来表示负数,但没有告诉我们2的补数是什么,尤其是没有告诉我们为什么加了一个“1”,而且实际上经常以错误的方式加。
这种混淆来自于对补数定义的不理解。补语是指使某物完整的缺失部分。
然而,在二进制中,求一个基数的补并不像求它的消简基数补那么容易,消简基数补定义为(b^n-1)-y,只比基数补小1。要得到一个减少的基数补,只需翻转所有的数字。
100 -> 011(减基数补位)
为了得到基数(2的)补,我们只需按定义加1。
011 +1 ->100(2的补码)。
/*开始文森特
这个数从1开始,所以它是负的,所以我们找到1111的补,也就是0000。 0000加上1,得到0001。 将0001转换为十进制,即1。 应用符号= -1。 大作。< / p >
结束文森特*/
应该理解为
数字从1开始,所以是负的。所以我们知道它是x的一个2的补。为了找到由它的2的补表示的x,我们首先需要找到它的1的补。
2的x的补码:1111 x的补数:1111-1 ->1110; X = 0001,(翻转所有数字)
应用符号-,结果=-x =-1。
参考:https://www.cs.cornell.edu/~tomf/notes/cps104/twoscomp.html
我把所有位的倒数加1。编程:
// in C++11 int _powers[] = { 1, 2, 4, 8, 16, 32, 64, 128 }; int value=3; int n_bits=4; int twos_complement = (value ^ ( _powers[n_bits]-1)) + 1;
Two的补语主要用于以下原因:
我读了一个奇妙的解释在Reddit由jng,使用里程表作为类比。
这是一个有用的约定。同样的电路和逻辑操作 在二进制中加减正数仍然对正数有效 如果按照惯例是负数,这就是为什么它是这样的 想象一下一辆汽车的里程表,它的转速是99999。如果你 增加00000,得到00001。如果减去00000,就得到99999 (由于翻滚)。如果你把1加回99999,它就回到 00000. 所以确定99999代表-1是很有用的。同样,确定99998表示-2是非常有用的,依此类推。你有 停一下,按照惯例,是数字的上半部分 被认为是负的(50000-99999),而下半部分是正的 只代表自己(00000-49999)。结果,上面的数字 5-9表示表示的数字为负,为0-4 表示所表示的是正的-与顶部的位完全相同 表示二进制补数中的符号 理解这一点对我来说也很难。有一次我拿到了,又回到 重新阅读书籍、文章和解释(当时没有互联网 当时),事实证明,许多描述它的人并不是真的 理解它。之后我确实写了一本教授汇编语言的书 那个(确实卖了10年).
2对给定数字的补码是no。通过1和no的1的补数相加得到。 假设,我们有一个二进制不。: 10111001101 它的1的补码是:01000110010 它的2的补码是:01000110011
按位补一个数就是将其中的所有位翻转。对2的补位,我们翻转所有的位,加1。
对有符号整数使用2的补码表示,我们应用2的补码操作将正数转换为负数,反之亦然。因此,以nibbles为例,0001(1)变成1111(-1),并再次应用该op,返回0001。
零处操作的行为有利于给出零的单一表示,而无需特别处理正零和负零。0000补充1111,当添加1时。溢出到0000,给我们一个0,而不是一个正数和一个负数。
这种表示的一个关键优点是,用于无符号整数的标准加法电路在应用于它们时产生正确的结果。例如,在nibbles中添加1和-1:0001 + 1111,位溢出寄存器,留下0000。
0001 + 1111
作为一个温和的介绍,优秀的Computerphile制作了关于这个主题的视频。
35=035=000000035.
在计算机存储位置没有空白空间。所有位(二进制数字)必须为0或1。为了有效地使用内存,可以将数字存储为8位、16位、32位、64位、128位表示形式。当存储为8位数字的数字被传输到16位位置时,符号和幅度(绝对值)必须保持不变。1的补语和2的补语表示都有助于实现这一点。 作为名词: 1的补位和2的补位都是有符号量的二进制表示,其中最有效的位(左边的位)是符号位。0表示正,1表示负。 2s补不等于负。它表示一个有符号的量。在十进制中,大小表示为正数。该结构使用符号扩展来保存数量,当提升到一个寄存器[],有更多的比特:
[0101]=[00101]=[00000000000101]=5 (base 10) [1011]=[11011]=[11111111111011]=-5(base 10)
作为动词: 2的补码是否定。这并不意味着消极。意思是如果负数变成正数;如果是正的就是负的。幅度是绝对值:
if a >= 0 then |a| = a if a < 0 then |a| = -a = 2scomplement of a
1的补数的官方方法是每一位数用1减去它的值。
1'scomp(0101) = 1010.
Example 1 Example 2 0101 --original number 1101 1's comp 1010 0010 add 1 0001 0001 2's comp 1011 --negated number 0011
在这些例子中,否定也适用于符号扩展数。
减法:
1110 Carry 00000 Carry 0110 is the same as 00110 -0111 +11001 ---------- ---------- sum 0101 sum 11111
请注意,当使用2的补码时,数字左侧的空白区域对于正数用0填充,而对于负数用1填充。进位总是被加上,必须是1或0。
干杯
2的补本质上是一种求二进制数的加性逆的方法。问自己这样一个问题:给定一个二进制形式的数字(存在于固定长度的内存位置),当将什么位模式添加到原始数字(在固定长度的内存位置)时,会使结果全为0 ?(在相同的固定长度存储器位置)。如果我们能得到这个位模式,那么这个位模式就是原始数字的-ve表示(加性逆);根据定义,将一个数与它的加性逆相加总是零。例如:取一个8位字节内的101为5。现在的任务是提出一个位模式,当添加到给定的位模式(00000101)会导致在存储5的内存位置全为0吗时,即字节的所有8位都应该为零。要做到这一点,从101的最右边的位开始,对于每个单独的位,再次问同样的问题:我应该向当前位添加哪位以使结果为零?考虑到通常的结转,继续这样做。在我们处理完最右3位(定义原始数字而不考虑前导零的数字)后,最后进位进入加性逆的位模式。此外,由于我们将原始数字保存在一个8位字节中,加法逆中的所有其他前导位也应该是1,因此(这很重要)当计算机添加“;(使用8位模式表示)和它的加法逆使用"that"存储类型(一个字节),则结果在那个字节中将为全零。
1 1 1 ---------- 1 0 1 1 0 1 1 ---> additive inverse --------- 0 0 0
简单来说,2's Complement是一种在计算机内存中存储负数的方法。而正数则存储为普通二进制数。
2's Complement
让我们考虑这个例子,
计算机使用Binary Number System表示任何数字。
Binary Number System
x = 5;
它表示为0101。
0101
x = -5;
当计算机遇到-符号时,它会计算它的2的补数并存储它。 i.e 5 = 0101,它的2的补码是1011.
-
i.e
1011
计算机处理数字的重要规则是:
negative
-0
1000 is not -0
8
positive number
2的补码是表示负数的一种方式,大多数控制器和处理器都以2的补码形式存储负数
问题是“什么是“2的补”?”对于那些想要从理论上理解它的人(以及我试图补充其他更实际的答案),简单的回答是:2的补码是对偶系统中负整数的表示,不需要额外的字符,如+和-。