什么是“2的补充”?
我参加了计算机系统课程,并且一直在挣扎 ,部分与Two的补充 。 我想了解它,但是我读过的所有东西都没有为我带来这幅画。 我已阅读维基百科文章和其他各种文章,包括我的课本 。
因此,我想开始这个社区维基的post来定义什么是补码是什么,如何使用它,以及它如何影响数字操作(如从签名到无符号,反之亦然),按位操作和位移操作。
我所希望的是一个清晰简洁的定义 ,程序员很容易理解。
二进制补码是存储整数的一种聪明方式,所以常见的math问题实现起来非常简单。
要了解,你必须考虑二进制数字。
它基本上说,
- 为零,使用全0。
- 对于正整数,开始向上计数,最大值为2 (位数-1) -1。
- 对于负整数,做同样的事情,但切换0和1的angular色(所以而不是以0000开始,从1111开始 – 这是“补充”部分)。
让我们用一个4位的小字节(我们称之为半字节 – 1/2字节)来尝试。
-
0000
– 零 -
0001
– 一个 -
0010
– 二 -
0011
– 三 -
0100
到0111
– 四到七
就我们所能做的积极的事情而言。 2 3 -1 = 7。
对于否定:
-
1111
– 负面的 -
1110
– 负面的两个 -
1101
– 负三 -
1100
到1000
– 负四到负八
请注意,对于负值( 1000
= -8),您会得到一个额外的值,您不会为正值。 这是因为0000
用于零。 这可以认为是电脑的号码线 。
区分正数和负数
这样做,第一位得到“符号”位的作用,因为它可以用来区分正数和负数。 如果最高有效位是1
,那么二进制可以被认为是负数,如果最高有效位(最左边)是0
,则可以认为十进制值是正数。
我想知道是否可以解释比维基百科的文章更好。
你用二进制补码来解决的基本问题是存储负整数的问题。
首先考虑以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.更大的问题是加减二进制数。 使用符号幅度加减的电路将非常复杂。
什么是
0010 1001 + ----
?
另一个系统是过多的符号 。 你可以存储负数,你摆脱了两个零的问题,但加减仍然是困难的。
所以顺便补充一下。 现在可以存储正整数和负整数,并且相对容易地执行算术。 有很多方法可以将数字转换成二进制补码。 这是一个。
将十进制转换为二的补码
-
将数字转换为二进制(现在忽略符号),例如5是0101,-5是0101
-
如果这个数字是一个正数,那么你就完成了。 例如5是使用二进制补码符号的二进制0101。
-
如果这个数字是负数那么
3.1find补码(0和1的倒数),例如-5是0101,所以find补码是1010
3.2补码1010 + 1 = 1011加1,因此,二补码中的-5是1011。
那么,如果你想在二进制中做2 +(-3)呢? 2 +( – 3)是-1。 如果您使用符号幅度来添加这些数字,您将需要做什么? 0010 + 1101 =?
用补码来考虑它会是多么容易。
2 = 0010 -3 = 1101 + ------------- -1 = 1111
将二的补码转换为十进制
将1111转换为十进制:
-
数字从1开始,所以是负数,所以我们find1111的补码,即0000。
-
加1到0000,我们得到0001。
-
将0001转换为十进制,即1。
-
应用sign = -1。
田田!
就像我见过的大多数解释一样,上面的解释清楚了如何使用2的补码,但是并没有真正地解释它们在math上的含义。 我会尽力做到这一点,至less对于整数来说,我会先介绍一些可能熟悉的背景。
回想一下,它是如何工作的小数:
2345
是一种写作方式
2 ×10 3 + 3 ×10 2 + 4 ×10 1 + 5 ×10 0 。
同样,二进制是一种用0和1写数字的方法,遵循相同的概念,但用2s代替上面的10。 然后在二进制中,
1111
是一种写作方式
1 ×2 3 + 1 ×2 2 + 1 ×2 1 + 1 ×2 0
如果你解决这个问题,结果是15(基数10)。 那是因为它
8 + 4 + 2 + 1 = 15。
对于正数来说,这一切都很好。 它甚至适用于负数,如果你愿意在他们面前贴一个负号,就像人类用十进制数字一样。 这甚至可以用计算机来完成,但是自从20世纪70年代初以来,我还没有见过这样的计算机。 我将留下不同讨论的理由。
对于计算机来说,使用补数表示法来表示负数更有效。 这是经常被忽视的东西。 补码表示涉及某种数字的数字的逆转,甚至是在正常正数之前的隐含的零。 这很尴尬,因为问题出现了:所有这些? 这可能是无限的数字要考虑。
幸运的是,电脑并不代表无限。 数字被限制在一个特定的长度(或宽度,如果你喜欢的话)。 所以让我们回到正确的二进制数字,但具有特定的大小。 我将使用这些例子的8位数字(“位”)。 所以我们的二进制数真的是
00001111
要么
0 ×2 7 + 0 ×2 6 + 0 ×2 5 + 0 ×2 4 + 1 ×2 3 + 1×2 2 + 1 ×2 1 + 1 ×2 0
为了形成2的补码阴性,我们首先补充所有的(二进制)数字来形成
11110000
并加1来形成
11110001
但是我们怎么理解这个意思是-15?
答案是我们改变了高阶位的含义。 这个位将是所有负数的1 。 改变将改变其贡献的标志,以显示它的数字的价值。所以现在我们的11110001被理解为代表
– 1 ×2 7 + 1 ×2 6 + 1 ×2 5 + 1 ×2 4 + 0 ×2 3 + 0×2 2 + 0 ×2 1 + 1 ×2 0
注意那个expression式前面的“ – ”? 这意味着符号位承载-2 7 ,即-128(基数10)。 所有其他位置保持与无符号二进制数字相同的权重。
制定我们的-15,这是
-128 + 64 + 32 + 16 + 1
试试你的计算器。 这是-15。
在我看到的三种主要方式中,电脑中出现了负数,2的补码为了便于使用而胜出。 虽然这有一个奇怪的地方。 由于它是二进制的,所以必须有偶数个可能的位组合。 每个正数可以与负数配对,但只有一个零。 否定一个零让你零。 所以还有一个组合,符号位为1 ,其他位置为0 。 相应的正数不适合正在使用的位数。
这个数字更奇怪的是,如果你试图通过补充和增加一个来形成积极的东西,你会得到相同的负数。 零这样做似乎很自然,但这是意想不到的,而且根本不是我们习惯的行为,因为计算机在旁边,我们通常想到的是数字的无限供给,而不是这个固定长度的算术。
这就像一个古怪的冰山一angular。 还有更多的人在等待下面的表面,但这就足够了这个讨论。 如果你研究定点算术的“溢出”,你可能会发现更多。 如果你真的想进入它,你也可以研究“模块化算术”。
2的补码对于find一个二进制值是非常有用的,但是我想到了一个更加简洁的解决这个问题的方法(从来没有人看到其他人发布它):
采取一个二进制,例如:1101 [假设空间“1”是符号]等于-3 。
使用2的补码,我们会这样做…将1101翻转为0010 …将0001 + 0010 ===>赋给我们0011. 0011为正数二进制= 3,因此1101 = -3 !
我意识到:
而不是所有的翻转和添加,你可以做基本的方法来解决一个积极的二进制(可以说0101)是(2 3 * 0)+(2 2 * 1)+(2 1 * 0)+(2 0 * 1)= 5。
做一个负面的完全相同的概念!(有一个小的扭曲)
以1101为例,
对于第一个数字而不是2 3 * 1 = 8 ,do – (2 3 * 1)= -8 。
然后照常继续,做-8 +(2 2 * 1)+(2 1 * 0)+(2 0 * 1)= -3
想象一下,你有有限的比特数/字节/数字/任何。 你把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。
这个表示是十进制补码 。 计算机通常使用二进制补码 ,除了使用位而不是数字之外,它们是相同的。
十进制补码的好处是加法正常 。 你不需要做任何特别的事情来增加正数和负数!
通过给定数字加1到1的补数找出两个补数。 假设我们必须找出10101
二进制补码,然后find它的补码,即01010
对这个结果加1
,即01010+1=01011
,这是最终的答案。
让我们用8位二进制forms得到答案10-12:我们真正要做的是10 +(-12)
我们需要得到12的恭维部分从10中减去它。二进制中的12是00001100.二进制中的10是00001010。
为了得到12的恭维部分,我们只需将所有的位反转,然后加上1.二进制反转的12是11110011.这也是反码(补码)。 现在我们需要添加一个,现在是11110100。
所以11110100是12的恭维! 当你这样想的时候很容易。
现在你可以用二进制的forms解决上述的10-12问题。
00001010 11110100 ----------------- 11111110
从math的angular度来看这个二进制补码系统是非常有意义的。 十来补充,这个想法是基本上“隔离”的差异。
例如:63 – 24 = x
我们加上24的补码,其实就是(100 – 24)。 所以我们所做的只是在等式两边加100。
现在的方程是:100 + 63 – 24 = x + 100,这就是为什么我们删除100(或10或1000或其他)。
由于不得不从一长串零中减去一个数字,我们使用了一个“减小的基数补码”系统,在十进制系统中,九补码。
当我们从一个大的九连环中减去一个数字时,我们只需要反转这个数字。
例如:99999 – 03275 = 96724
这就是原因,在九的补充之后,我们加1。如你从童年的math中可能知道的那样,9通过'偷'来变成10。所以基本上这只是10的补码而已。
在二进制中,二进制补码等于十进制补码,而对九进制补码进行补码。 主要的区别在于,我们不是试图用10的幂(在方程中加10,100等)来区分这种差别,而是试图用两个幂来区分这个差异。
正是由于这个原因,我们反转了这些位。 就像我们的被减数如何是十进制的九连环,我们的被减数就是一连串的二进制。
例如:111111 – 101001 = 010110
因为1的链条是低于2的一个很好的权力,所以它们像十进制那样从差异中“偷走”1。
当我们使用负数的二进制数时,我们只是在说:
0000 – 0101 = x
1111 – 0101 = 1010
1111 + 0000 – 0101 = x + 1111
为了“隔离”x,我们需要加1,因为1111是一个远离10000的,我们删除了前导1,因为我们只是把它加到了原来的差异中。
1111 + 1 + 0000 – 0101 = x + 1111 + 1
10000 + 0000 – 0101 = x + 10000
只要从两边删除10000就可以得到x,这是基本的代数。
许多答案很好地解释了为什么用二进制补码来表示负数,但是不要告诉我们二进制补码是什么,特别不是为什么添加“1”,事实上通常是以错误的方式添加的。
这个混淆来自对补码数字定义的不了解。 补充是完成某件事情的缺失部分。
根据定义,以b为单位的n数字x的基数补码是b ^ nx。 在二进制中,4表示100,其具有3位(n = 3)和2(b = 2)的基数。 所以它的基数补数是b ^ nx = 2 ^ 3-4 = 8-4 = 4(或二进制的100)。
然而,在二进制中,获得基数的补码并不像获得其基数补码减less那样容易,其被定义为(b ^ n-1)-y,仅比基数补码小1。 为了得到一个减less的基数补码,你只需要翻转所有的数字。
100 – > 011(减(补)数)
为了得到基数(二)的补码,我们只需加上1,就像定义的那样定义了。
011 +1 – > 100(二补)。
现在我们来看看Vincent Ramdhanie给出的例子(参见上面的第二个回答)
/ *文森特的开始
将1111转换为十进制:
数字从1开始,所以它是负数,所以我们find1111的补码,它是0000.将1加到0000,我们得到0001.将0001转换成十进制,这是1.应用sign = -1。 田田!
文森特结束* /
应该被理解为
数字从1开始,所以是负数。 所以我们知道这是某个值x的二次补数。 为了find由它的二进制补码表示的x,我们首先需要find它的1的补码。
x的二进制补码:1111 x的补码:1111-1 – > 1110; x = 0001,(翻转所有数字)
应用符号 – 和答案= -x = -1。
这是一种巧妙的编码负整数的方法,数据types的位的大约一半的组合被保留为负整数,并且大多数负整数与其相应的正整数相加导致进位溢出结果是二进制零。
因此,如果一个是0x0001,那么在2的补码中,则-1是0x1111,因为这将导致总和为0x0000(溢出为1)。
2的补充:当我们添加一个数字的补码1时,我们会得到2的补码。 例如:100101它的1的补码是011010,2的补码是011010 + 1 = 011011(通过添加一个1的补码) 欲了解更多信息,这篇文章以graphics的方式解释。
我喜欢拉维尼奥的回答,但是移位会增加一些复杂性。 在尊重符号位或不尊重符号位的情况下,通常可以select移动位。 这是将数字视为有符号数字(半字节为-8到7,字节为-128到127)或全范围无符号数字(半字节0到15,字节0到255)之间的select。
几个星期前我也有同样的问题。 我最终从各种来源上在网上阅读,试图把这些东西放在一起,并自己写下来,以确保我正确理解它。 主要有两个原因我们使用二进制补码:
- 避免多个表示0
- 为了避免在溢出的情况下跟踪进位(如补码)。
- 进行加减等简单的操作变得容易。
如果你想要更详细的解释这个问题,请试试我在这里写的文章。 希望它有帮助!
参考: 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;
您还可以使用在线计算器来计算十进制数的二进制补码二进制表示forms: http : //www.convertforfree.com/twos-complement-calculator/
最简单的答案是:
1111 + 1 =(1)0000。 所以1111必须是-1。 然后-1 + 1 = 0。
理解这些对我来说是完美的。