确定一个数字是否可以表示为n位整数(二进制补码)

时间:2014-07-21 10:14:39

标签: c bit-manipulation

我很难理解如何处理C中的位,关于2的补码。

这是家庭作业的一部分,但我不是在寻找代码答案,而是要了解用两个补码表示的内容。

我的任务是将一个数字限制为一定数量的比特(n),并确定给定数字是否可以用n个比特中的两个补码表示。

根据示例,5不能表示为3位整数,而-4 CAN表示为3位整数。

为什么会这样?


编辑:我对我的思维过程进行了详细的解释,但意识到我已经完全离开,所以决定省略它。

我最初的推理是看是否允许5和-5,4和-4以3位表示是否有意义。但这并没有意义,因为它并没有真正回答这个问题。

我理解5和-4是如何表示为2的补码。例如,4位:

5:0101

-4:1100


第二次编辑:

为澄清,决定加上我原来的推理:

5是0101,-5是1011.
我可以看到当限制为3位时,5不能用2的补码表示,因为没有第4位,我们不能指示-5是负数。我们需要在1011中额外增加1个。如果我们最多只能有3个比特,那么我们就有011,并且没有办法将-5与3区分开来,后者是4中的0011位,和3位的011。  这种推理是否正确?

4是0100,-4是1100.
在这里,我很困惑。我不明白为什么-4可以用3位表示为2的补码整数。

4表示为0100,100表示​​为3位。 -4是,如果我们从4(100)开始,我们翻转100(011),并加1(100),我们再次留下100(3位)。在4位中,我认为这表示为1100。

我的困惑是,我们不需要额外的1,1100,以区分-4和4,即0100?如果我们只有3位,我们如何区分100和100?

1 个答案:

答案 0 :(得分:4)

为了理解这一点,有必要记住,尽管它在现代硬件上无处不在,但它们不是自然法则,而是人类构造。

假设我们要将整数打包成n位。设n = 3以简化和简洁。很明显,我们可以选择任何2 3 = 8个整数,只要我们保持一致,但是在算术方面,有些选择会让生活更轻松。

无符号整数是直接的。有一个自然的映射:

Encoding  Value
 000        0
 001        1
 010        2
 011        3
 100        4
 101        5
 110        6
 111        7

这只是值的二进制编码,填充为零。零的所有位都为零,这很方便。加法和减法只是工作,模2 n

有符号整数比较棘手。在普遍采用二进制补码之前,还使用了至少两个其他表示:带符号量(SM)和 1'补码(1C)。

Encoding   SM    1C
 000        0     0 
 001        1     1
 010        2     2
 011        3     3
 100       -0    -3
 101       -1    -2
 110       -2    -1
 111       -3    -0

两种编码SM和1C使用n位来存储从 - (2 n-1 -1)到2 n-1 -1的有符号整数。两种编码都以与以前相同的方式存储正整数。好:两者在正反方向上走的是相同的距离。好:两者都容易进行负面测试(除零之外,通常需要特殊处理)。好:两者都使数字变得容易(SM:翻转符号位; 1C:翻转所有位)。坏:两者都包括一个不必要的“负零”,这使得测试平等变得复杂,以及算术。

这使我们得到了两个补语(2C)。

Encoding   2C
 000        0
 001        1
 010        2
 011        3
 100       ???
 101       -3
 110       -2
 111       -1

这里我们像以前一样从正整数开始,通过从零开始“向后计数”直到我们在中间相遇来得到负整数。好:这使得算术非常自然,就像在未签名的情况下一样容易。坏:否定比SM或1C更难。

但是我们怎么处理???的行呢?我们已经拥有1C和SM中的所有数字。我们可以选择4,或-4,或者可能是“未定义”。 2C中的惯例是我们选择负值。这给出了-4至+3,或更通常-2 n-1 至2 n-1 -1的范围。这个不对称的范围很尴尬(我们有-4但不是+4,结果是-4是它自己的负数![1]),但保留了所有负数都设置了最高位的属性,以及确保每个编码都有一个与之关联的值。

所以(最后!)你的问题。为什么-4可以代表 在2的补码作为3位整数? 因为那是最不好的选择。

进一步阅读:维基百科上的signed number representations


[1]这是一个测试程序,用于演示这是多么混乱。这里int是32位宽。

[~]% cat 2c.c
#include <stdio.h>
#include <limits.h>

int main(void) {
    int i = INT_MAX;
    int j = INT_MIN;

    printf ("   i = % d (hex %x)\n", i, i);
    printf ("   j = % d (hex %x)\n", j, j);
    printf ("  -i = % d (hex %x)\n", -i, -i);
    printf ("  -j = % d (hex %x)\n", -j, -j);
    printf ("i*-1 = % d (hex %x)\n", i*-1, i*-1);
    printf ("j*-1 = % d (hex %x)\n", j*-1, j*-1);
    printf ("i/-1 = % d (hex %x)\n", i/-1, i/-1);
    printf ("j/-1 = % d (hex %x)\n", j/-1, j/-1);

    return 0;
}

请注意,根据C标准,否定最负整数(INT_MIN)是未定义的行为,因此使用三种否定方法中的一种杀死程序是完全合理的:

[~]% clang -Wall 2c.c -o 2c && ./2c
   i =  2147483647 (hex 7fffffff)
   j = -2147483648 (hex 80000000)
  -i = -2147483647 (hex 80000001)
  -j = -2147483648 (hex 80000000)
i*-1 = -2147483647 (hex 80000001)
j*-1 = -2147483648 (hex 80000000)
i/-1 = -2147483647 (hex 80000001)
zsh: floating point exception  ./2c

这是security vulnerabilities的来源(遗憾的是,大多数真实的人都没有获得有趣的演练)。