代码之家  ›  专栏  ›  技术社区  ›  Brian

__2的补码__是什么?

  •  382
  • Brian  · 技术社区  · 7 年前

    我在上计算机系统课程 挣扎 部分地与 Two's Complement . 我想理解它,但我读到的每一件事都没有把这幅画给我带来。我读过 wikipedia article 以及其他各种物品,包括 my text book .

    因此,我想开始 社区维基 post定义二者的补码是什么,如何使用它,以及它如何在强制转换(从有符号到无符号,反之亦然)、位操作和位移位操作等操作中影响数字。

    我希望的是 清晰简洁的定义 这很容易被程序员理解。

    19 回复  |  直到 7 年前
        1
  •  560
  •   lavinio    7 年前

    二的补码是一种巧妙的整数存储方法,因此常见的数学问题很容易实现。

    要理解,你必须想到二进制的数字。

    基本上说,

    • 对于零,全部使用0。
    • 对于正整数,开始计数,最大值为2。 (位数-1) - 1。
    • 对于负整数,执行完全相同的操作,但是切换0和1的角色(因此,不是从0000开始,而是从1111开始-这是“补码”部分)。

    让我们用一个4位的小字节(我们称之为 nibble - 1/2字节。

    • 0000 -零
    • 0001
    • 0010
    • 0011 -三
    • 0100 0111 -四至七

    这是我们在积极方面所能做到的。二 1=7。

    否定词:

    • 1111 -负一
    • 1110 -否定二
    • 1101 -负三
    • 1100 1000 -负4到负8

    注意,你会得到一个额外的负值。( 一千 =-8)你不赞成正面评价。这是因为 0000 用于零。这可以被认为是 Number Line 计算机。

    区分正数和负数

    这样,第一个位得到了“符号”位的作用,因为它可以用来区分正十进制值和负十进制值。如果最高有效位是 1 ,那么二进制可以说是负的,其中最高有效位(最左边)是 0 可以说,辨别十进制值是正的。

    “恭维”负数只是翻转符号位,然后从0开始计数。但是这种方法必须处理口译 一千 作为“负零”,这很令人困惑。在靠近硬件工作时,通常只需要担心这一点。

        2
  •  303
  •   Vincent Ramdhanie    10 年前

    我想知道这是否能比维基百科的文章更好地解释。

    您试图用二的补码表示法解决的基本问题是存储负整数的问题。

    首先考虑一个以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 +
    ----
    

    ?

    另一个系统是 excess notation . 你可以存储负数,你可以去掉两个零的问题,但是加减仍然很困难。

    所以接下来是二的补充。现在您可以存储正整数和负整数,并相对容易地执行算术。有许多方法可以将一个数字转换为二的补码。这里有一个。

    将十进制转换为2的补码

    1. 将数字转换为二进制(暂时忽略符号) 例如,5是0101,-5是0101

    2. 如果数字是正数,则完成。 例如,5是二进制的0101,使用两个补码表示法。

    3. 如果数字为负数,则

      3.1求补码(倒转0和1) 例如-5是0101,所以求补码是1010。

      3.2在补码1010+1=1011中加1。 因此,2的补码中的-5是1011。

    那么,如果你想做2+(-3)二进制呢?2 +(-3)为-1。 如果你用符号大小加上这些数字,你会怎么做?0010+1101=?

    使用二者的补码可以考虑它有多容易。

     2  =  0010
     -3 =  1101 +
     -------------
     -1 =  1111
    

    将二的补码转换为十进制

    将1111转换为十进制:

    1. 这个数字以1开头,所以它是负数,所以我们找到了1111的补码,它是0000。

    2. 加1到0000,得到0001。

    3. 将0001转换为十进制,即1。

    4. 应用符号=-1。

    塔达!

        3
  •  110
  •   ForDummies    8 年前

    像我看到的大多数解释一样,上面的解释清楚如何使用2的补码,但并不能真正解释它们是什么。 数学上。我会尝试这样做,至少对于整数,我会先介绍一些可能熟悉的背景。

    回想一下它对小数的作用:
    二千三百四十五
    是一种写作方式
    ×10 + ×10 + ×10 + ×10 .

    同样,二进制是一种使用 按照同样的想法,但用2s替换上面的10s,然后用二进制,
    一千一百一十一
    是一种写作方式
    ×2 + ×2 + ×2 + ×2
    如果你计算出来,等于15(以10为基数)。那是因为它是
    8+4+2+1=15。

    这对正数来说是很好的。如果你愿意把一个减号放在负数前面,它甚至可以对负数起作用,就像人类对待十进制数字一样。这甚至可以在计算机上实现,有点像,但我从20世纪70年代初就没有见过这样的计算机。我将留下不同讨论的原因。

    对于计算机来说,使用 补码 负数的表示法。这里有一些经常被忽视的东西。补码符号涉及到数字数字的某种倒转,甚至是在正常正数之前的隐含零。这很尴尬,因为问题出现了:所有人?可以是无限多的数字。

    幸运的是,计算机并不代表无穷大。数字被限制在特定的长度(或者宽度,如果您愿意的话)。那么让我们回到正的二进制数,但是有一个特定的大小。我将使用8位(“位”)作为这些示例。所以我们的二进制数
    00001111

    ×2 + ×2 + ×2 + ×2 + ×2 + 1×2 + ×2 + ×2

    为了形成2的补码负数,我们首先对要形成的所有(二进制)数字进行补码。
    一千一百一十一万
    在表单中添加1
    一千一百一十一万零一
    但我们如何理解这意味着-15?

    答案是我们改变了高阶位(最左边的位)的含义。这个位将是一个 所有负数。更改将更改其对显示的数字值的贡献符号。所以现在我们的 一千一百一十一万零一 被理解为代表
    - ×2 + ×2 + ×2 + ×2 + ×2 + 0×2 + ×2 + ×2
    注意到那个表达式前面的“—”?这意味着符号位承载重量-2 ,即-128(以10为基数)。所有其他的位置都保持着它们在无符号二进制数中的相同权重。

    计算出我们的-15,它是
    -128+64+32+16+1
    在计算器上试试。现在是15点。

    在我所看到的三种主要的计算机表示负数的方法中,2的补码为方便一般使用而赢得了胜利。不过,这有点奇怪。因为它是二进制的,所以可能的位组合必须是偶数。每个正数都可以与其负数配对,但只有一个零。否定零等于零。所以还有一个组合,数字和 在符号位和 其他任何地方。相应的正数不适合使用的位数。

    这个数字更奇怪的是,如果你试图通过补加一来形成它的正数,你会得到相同的负数。零可以做到这一点似乎是很自然的,但这是出乎意料的,完全不是我们习惯的行为,因为除了计算机之外,我们通常认为数字的供应是无限的,而不是固定长度的算术。

    这就像是奇怪事物的冰山一角。在表面之下还有更多的等待,但这已经足够进行讨论了。如果研究定点算术的“溢出”,您可能会发现更多。如果你真的想了解它,你也可以研究“模块化算术”。

        4
  •  18
  •   Simon Yundov    13 年前

    2的补码对于查找二进制的值非常有用,但是我想到了一种更简洁的方法来解决这个问题(从未见过其他人发布它):

    以二进制为例:1101(假设空格“1”是符号)等于 - 3 .

    使用2的补码,我们可以这样做…将1101翻到0010…加上0001+0010==>得到0011。0011为正二进制=3。因此1101= - 3 !

    我意识到:

    不需要所有的翻转和添加,您只需要做一个求解一个正二进制的基本方法(比如0101)是(2) * 0)+(2) * 1)+(2) * 0)+(2) * 1)=5。

    用一个否定的词来做完全相同的概念!(有点扭曲)

    以1101为例:

    对于第一个数字而不是2 * 1= DO—(2) * 1) - 8 .

    然后像往常一样继续做 - 8 +(2) * 1)+(2) * 0)+(2) * 1) - 3

        5
  •  13
  •   Captain Segfault    17 年前

    假设您有一个有限的位数/trits/digits/whatever。您将0定义为所有数字均为0,并自然向上计数:

    00
    01
    02
    ..
    

    最终你会溢出。

    98
    99
    00
    

    我们有两个数字,可以表示0到100之间的所有数字。所有这些数字都是正数!假设我们也要表示负数?

    我们真正拥有的是一个循环。2之前的数字是1。1之前的数字是0。0之前的数字是… 九十九 .

    因此,为了简单起见,假设任何超过50的数字都是负数。”0“到”49“表示0到49。”99”是-1,“98”是-2,…”50“IS”50。

    此代表是 十的补码 . 计算机通常使用 二的补码 ,除了使用位而不是数字之外,其他都是相同的。

    10的补码的好处在于 只是作品 . 你不需要做任何特殊的事情来添加正数和负数!

        6
  •  5
  •   evaa    11 年前

    将给定数的一对一补码相加,得到两个补码。 假设我们必须找出 10101 然后找到它的补充,也就是说, 01010 添加 1 因此,也就是说, 01010+1=01011 ,这是最后的答案。

        7
  •  4
  •   Brian    13 年前

    让我们用8位二进制形式得到答案10_ 12: 我们真正要做的是10+(-12)

    我们需要得到12的赞美部分,从10中减去。 二进制中的12是00001100。 二进制中的10是00001010。

    为了得到12的赞美部分,我们只需颠倒所有的位,然后加1。 12在二进制倒转中是11110011。这也是反码(补码)。 现在我们需要增加一个,现在是11110100。

    所以11110100是12的赞美!你这样想很容易。

    现在你可以用二进制形式解决上面的10-12问题。

    00001010
    11110100
    -----------------
    11111110  
    
        8
  •  3
  •   KyBrooks    11 年前

    从数学的角度来看,这两种补码系统确实是有意义的。在10的补充中,基本上是“隔离”差异。

    示例:63-24=X

    我们加上24的补码,实际上就是(100-24)。所以,实际上,我们要做的就是在方程两边加100。

    现在的方程是:100+63-24=x+100,这就是为什么我们去掉100(或10或1000或其他)。

    由于从一个长的零链中减去一个数是不方便的,我们使用了一个“减数基数补码”系统,在十进制中是9的补码。

    当我们看到一个从九个大串中减去的数字时,我们只需要把数字倒过来。

    示例:99999-03275=96724

    这就是为什么,在补足9后,我们加1。你可能从儿时的数学中知道,9通过“偷”1变成10。所以基本上,就是10的补码,从差额中减去1。

    在二进制中,二的补码等于十的补码,一的补码等于九的补码。主要的区别在于,我们不是试图用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,这是基本代数。

        9
  •  3
  •   user779764    9 年前

    到目前为止,许多答案都很好地解释了为什么用二的补码来表示负数,但不要告诉我们二的补码是什么,尤其不是为什么加了一个‘1’,事实上经常以错误的方式加。

    这种混淆源于对补码的定义理解不足。补语是使某件事情完整的缺失部分。

    根据定义,n位数x在基数b中的基数补码是b^n-x。 在二进制4中,用100表示,它有3位(n=3)和2的基数(b=2)。所以它的基数补码是b^n-x=2^3-4=8-4=4(或二进制的100)。

    然而,在二进制中,获得一个基数补码并不像得到它的减数基数补码那样容易,减数基数补码被定义为(b^n-1)-y,比基数补码少1。要得到一个减基数补码,只需翻转所有的数字。

    100->011(减(一)基数补码)

    为了得到基数(2的)补码,我们只需添加1,定义如下。

    011+1->100(两个补码)。

    现在,有了这个新的理解,我们来看看下面给出的示例 文森特·拉姆达尼(见上文第二条回复)

    /*文森特的开始

    将1111转换为十进制:

    这个数字以1开头,所以它是负数,所以我们找到了1111的补码,它是0000。 加1到0000,得到0001。 将0001转换为十进制,即1。 应用符号=-1。 塔达!

    文森特的结尾*/

    应该理解为

    数字以1开头,所以是负数。所以我们知道它是某个值x的2的补码。要找到用它的2的补码表示的x,我们首先需要找到它的1的补码。

    X的2补码:1111 X:1111-1->1110的补码; X=0001,(翻转所有数字)

    应用符号-和答案=-x=-1。

        10
  •  2
  •   Edwin Buck    14 年前

    这是一种巧妙的负整数编码方法,即数据类型的大约一半的位组合被保留给负整数,并且大多数负整数与其对应的正整数相加会导致进位溢出,使结果为二进制零。

    所以,在2的补码中,如果一个是0x001,那么-1就是0x1111,因为这将导致总和为0x0000(溢出为1)。

        11
  •  2
  •   Milon    11 年前

    2_ s补码:当我们用数字的1_ s补码添加一个额外的数字时,我们将得到2_ s补码。例如:100101 It__S 1_S补码为011010,2_S补码为011010+1=011011(加1补码) For more information 本文以图形的方式解释它。

        12
  •  1
  •   Nosredna    17 年前

    我喜欢拉维尼奥的回答,但移位会增加一些复杂性。通常在尊重符号位或不尊重符号位的情况下,可以选择移动位。这是将数字视为有符号数字(一个半字节为8到7,字节为128到127)或全范围无符号数字(一个半字节为0到15,字节为0到255)之间的选择。

        13
  •  1
  •   K.N. Bhargav    8 年前

    几周前我也遇到过同样的问题。最后,我从各种渠道在线阅读了关于它的文章,试图把这些文章拼凑起来,然后自己写出来只是为了确保我正确理解它。我们使用two的补码主要有两个原因:

    1. 避免0的多重表示
    2. 避免在溢出的情况下跟踪进位(如补码)。
    3. 执行简单的加法和减法运算变得容易。

    如果你想对手头的事情有更详细的解释,那就试试我写的那篇文章。 here . 希望它有帮助!

        14
  •  1
  •   Alister Norris    8 年前

    我读了一个很好的解释 on Reddit 用JNG,用里程表作类比。

    enter image description here

    这是一个有用的惯例。相同的电路和逻辑操作 加/减二进制中的正数对两个正数都有效 如果按惯例使用负数,这就是为什么 有用且无所不在。

    想象一下一辆汽车的里程表,它在(比如)99999转。如果你 递增00000,得到00001。如果你减少00000,你得到99999 (由于滚动)。如果将一个添加回99999,它将返回到 00000。所以确定99999代表-1是很有用的。同样地,确定99998代表-2是非常有用的,依此类推。你有 停在某个地方,也按照惯例停在数字的上半部分 为负(50000-99999),下半部分为正 只代表自己(00000-49999)。因此,最上面的数字 5-9表示负数,0-4 表示为正-与顶位完全相同 用二进制补码表示符号。

    我也很难理解这一点。一旦我得到它,然后回到 重新阅读书籍文章和解释(没有互联网 当时),很多描述它的人并不是真的 明白了。之后我写了一本教汇编语言的书 这一点(10年来确实很畅销)。

        15
  •  0
  •   Charles Thomas    9 年前

    参考文献: 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;
    
        16
  •  0
  •   2 revs<br/>user10862846&#13;    7 年前

    给定数的2的补数是将1与1的补数相加得到的数。 假设我们有一个二进制数字:10111001101 是1的补码是:01000110010 2的补码是:01000110011

        17
  •  0
  •   ahcox    7 年前

    按位补码就是把数字中的所有位翻转过来。为了两个s的补码,我们翻转所有位并添加一个。

    使用有符号整数的2_s补码表示法,我们应用2_s补码运算将正数转换为其负数等价物,反之亦然。因此,举个例子, 0001 (1)成为 1111 (-1)再次应用op,返回到 0001 .

    零操作的行为有利于在不特殊处理正零和负零的情况下给出零的单一表示。 0000 补语 一千一百一十一 ,当添加1时。溢出到 0000 给我们一个零,而不是一个正的和一个负的。

    这种表示的一个主要优点是,无符号整数的标准加法电路在应用于它们时会产生正确的结果。例如,在半字节中添加1和-1: 0001 + 1111 ,位溢出寄存器,留下 0000 .

    为了作一个温和的介绍,这位出色的计算机爱好者制作了一个 video on the subject .

        18
  •  -2
  •   Chad Davis    9 年前

    您还可以使用在线计算器计算十进制数的二进制补码表示: http://www.convertforfree.com/twos-complement-calculator/

        19
  •  -5
  •   dmitry    10 年前

    最简单的答案是:

    1111+1=(1)万。所以1111必须是-1。然后-1+1=0。

    我完全能理解这些。