C 确定数字是否可以表示为n位整数(两个&x27;s补码)

C 确定数字是否可以表示为n位整数(两个&x27;s补码),c,bit-manipulation,C,Bit Manipulation,关于二的补码,我有点难以理解如何使用C中的位 这是家庭作业的一部分,但我不是在寻找代码答案,而是想了解2的补码表示法是怎么回事 我的任务是将一个数字限制在一定数量的位(n)内,并确定给定的数字是否可以用n个位的2的补码表示 根据示例,5不能表示为3位整数,而-4可以表示为3位整数 为什么会这样 编辑:我对我的思维过程做了详细的解释,但意识到我完全错了,所以决定省略它 我最初的推理是看允许5和-5、4和-4用3位表示是否有意义。但这没有道理,因为这并不能真正解决问题 我理解5和-4如何表示为2的

关于二的补码,我有点难以理解如何使用C中的位

这是家庭作业的一部分,但我不是在寻找代码答案,而是想了解2的补码表示法是怎么回事

我的任务是将一个数字限制在一定数量的位(n)内,并确定给定的数字是否可以用n个位的2的补码表示

根据示例,5不能表示为3位整数,而-4可以表示为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位,后者是0011分4位,011分3位。 这个推理正确吗

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呢?

为了理解这一点,请记住,尽管在现代硬件中普遍存在,但2的补码不是自然法则,而是人类构造

假设我们想把一个整数压缩成n位。为了简单和简洁,设n=3。显然,只要我们是一致的,我们可以选择任何23=8的整数,但有些选择在算术方面比其他选择更容易

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

Encoding  Value
 000        0
 001        1
 010        2
 011        3
 100        4
 101        5
 110        6
 111        7
这只是值的二进制编码,带有零填充。零的所有位都是零,这很方便。加减法只起作用,模为2n

有符号整数更复杂。在普遍采用两个补码之前,至少还使用了另外两种表示法:符号幅度(SM)和一个补码(1C)

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

这就引出了2的补码(2C)

这里,我们先从正整数开始,然后从零开始计数“负”,直到我们在中间相遇。好:这使得算术非常自然,就像在无符号情况下一样简单。坏:否定比SM或1C更复杂

但我们该怎么处理这一排的?我们已经拥有1C和SM中的所有数字。我们可以选择4,或者-4,或者“未定义”。2C中的惯例是我们选择负值。这为我们提供了-4到+3的范围,或者更一般地说是-2n-1到2n-1-1。这种非对称范围很尴尬(我们有-4,但不是+4,事实证明-4是它自己的负数![1]),但保留了所有负数都有顶部位集的属性,并确保每个编码都有一个与之相关的值

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

进一步阅读:在维基百科上


[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;
}

这是一个原因(遗憾的是,大多数真实的人没有得到有趣的演练)。

为了理解这一点,请记住,尽管二者互补在现代硬件上无处不在,但它不是自然法则,而是人类构造

假设我们想把一个整数压缩成n位。为了简单和简洁,设n=3。显然,只要我们是一致的,我们可以选择任何23=8的整数,但有些选择在算术方面比其他选择更容易

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

Encoding  Value
 000        0
 001        1
 010        2
 011        3
 100        4
 101        5
 110        6
 111        7
这只是值的二进制编码,带有零填充。零的所有位都是零,这很方便。加减法只起作用,模为2n

有符号整数更复杂。在普遍采用两个补码之前,至少还使用了另外两种表示法:符号幅度(SM)和一个补码(1C)

SM和1C两种编码都使用n位来存储从-(2n-1-1)到2n-1-1的有符号整数。两种编码存储正整数的方式与以前相同。好:两者在正向和负向上的距离相同。好:这两种方法都可以使消极性测试变得容易(除了零,这通常需要特殊处理)。好的:机器人
[~]% 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;
}
[~]% 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