7

我仍然没有找到为什么最低的有符号负数没有等效的有符号正数的原因?为简单起见,我的意思是 3 位二进制数 100 是 -4?但我们不能有一个正4的签名格式,因为我们不能。它溢出了。那么我们如何知道二进制补码 1000 是 -4 1000 0000 是 -128 等等?我们没有原始的正数

4

9 回答 9

19

一种思考方式是有符号二进制补码格式的工作原理是为每个位分配一个 2 的幂,然后翻转最后一个 2 的幂的符号。我们以-4为例,它表示为100。这意味着该值是

-1 x 2^2 + 0 x 2^1 + 0 x 2^0

如果我们想得到这个值的正版本,我们必须否定它才能得到

 1 x 2^2 - 0 x 2^1 - 0 x 2^0

请注意,此值等于

 1 x 2^2 + 0 x 2^1 + 0 x 2^0

换句话说,这个值的正常二进制表示是 100。但是,我们在这里遇到了麻烦,因为我们使用了带符号的二进制补码表示,这意味着我们专门保留了 4 位作为符号位。因此,当我们尝试将位模式 100 解释为带符号的三位二进制补码值时,它会返回与我们开始时相同的结果。位的短缺是这里的痛处。

更一般地,给定 n 位,其中第一个是二进制补码表示中的符号位,尝试计算 -1000...00 将返回相同的值,因为存储大正值所需的位具有特殊的赋予它的意义。

那么为什么要这样做呢?这样做的原因是,如果您只有 n 位,则无法存储值 -2 n - 1到 2 n - 1,因为这里有 2 n + 1 个不同的数字并且只有 2^n 不同的位模式。因此,排除最大的正数可以在指定的位模式中保存所有不同的数字。

但是为什么要放弃高价值而不是低价值呢?这是为了保持与无符号整数的二进制兼容性。在无符号整数中,值 0 到 2 n-1 - 1 都使用标准的以二为底的表示进行编码。因此,为了使无符号整数和有符号整数完全一致,无符号整数被设计为与前 2 n - 1 个无符号整数逐位等效,其范围从 0 到 2 n - 1 - 1,包括0 到 2 n - 1 - 1 . 在此之后,无符号值需要最高有效位来编码数字,但有符号值使用此作为符号位。

希望这可以帮助!

于 2012-01-18T20:59:31.673 回答
14

-INT_MIN是整数溢出,在 C 中是未定义的行为。

-INT_MININT_MIN只有当有符号整数溢出换行时才保证等于。这可以通过gcc例如-fwrapv选项来启用。

编译器通常利用整数溢出是 C 中未定义的行为这一事实来执行一些优化。依赖包装的有符号整数溢出是不安全的。

一个众所周知的编译器优化示例如下

#define ABS(x) ((x) > 0 ? (x) : -(x)) 

void foo(int x){  
    if (ABS(x) >= 0) {
        // some code
    }
}

今天大多数启用了优化选项的编译器(gcc, )都会根据未定义行为icc这一事实来优化测试。-INT_MIN

于 2012-01-18T21:15:52.117 回答
3

A. n 位二进制数有偶数种可能性,因此我们不能表示正数和负数的相同范围。

B. 我们希望每个以 1 开头的数字都是负数,每个以 0 开头的数字都是非负数。(不是相反,因为我们希望在有符号和无符号中对正数和零表示相同的表示。正因为如此,0 是正数的一半,所以它们少了一个位置。

于 2012-01-18T20:59:48.993 回答
3

二进制补码的替代具有这样的性质,它被称为一个补码
在一个人的补码形式中,可能的最低值也具有有效的正数形式。


一个补码的工作原理是简单地将数字本身的所有位取反。
例如,我们知道0110 == 6and in one's complement 1001 == -6。使用一个补码,我们得到的正数和负数一样多。

但是位表示1111呢?只看它,我们就知道它是零(0000 = 0; 1111 = -0)的“负”形式,但这样的数字没有任何意义,而且有点浪费。

相反,我们使用二进制补码,它类似于一个补码,但在反转位之后,我们加一。因此,如果我们知道0110 = 6,那么一个补码是1001,而二进制补码是1001 + 1 == 1010。使用二进制补码,我们没有“负零”,因为它会导致溢出。

另一种看待它的方式是“如果设置了最高位,则数字为负”。这意味着正范围是[0 .. 2^(bits - 1)],负范围是其他一切。正数的数量与负数的数量相同,但是因为(在这种格式中)零被认为是正数,所以负数范围被移一到[-1 .. (neg) 2^(bits - 1)].


假设我们正在处理二进制补码中的 3 位有符号数。这会给我们下表:

BITS  VALUE
000       0
001       1
010       2
011       3

100      -4
101      -3
110      -2
111      -1

你可以看到正数和负数的数量相同,只是负数不像正数那样从 0 开始。

于 2012-01-18T21:10:24.767 回答
2

缺少的数字是0。在数学意义上,0 既不是正数也不是负数。但在二进制意义上,由于0没有负位,所以它被认为是正的。换句话说,如果你想要 -128 到 128,就不可能有 0。

于 2012-01-18T20:58:39.583 回答
1

因为您必须数 0。整数范围 [-4,-1](或等效地 -4,-3,-2 和 -1)包含 4 个数字,范围 [0,3] 的其余部分(或,等效地,0、1、2 和 3) 包含 4 个数字,总共 8 个,3 位二进制数具有 2 的 3 次幂 (=8) 可能的组合。

这样想吧。[-n,+n] 形式的任何整数范围都必须具有奇数大小(2*n+1 个整数)。无论您使用什么整数二进制表示,都会有不同数量的负数和正数,因为组合的数量总是偶数(2 的幂)。

于 2012-01-18T20:59:35.717 回答
0

那么我们如何知道二进制补码 1000 是 -4 1000 0000 是 -128 等等?我们没有原始的正数

您的错误是认为我们需要正数的二进制补码表示来计算负数的二进制补码表示。

求负数的补码的过程是:

从要表示的数字的绝对值的正常非二进制补码表示开始。所以对于-4,取|-4|的非二进制补码表示,100。

翻转所有位:100 -> 011 (或 ...11111011 无限期地向左继续)。

加一:011 -> 100(或 ...11111100)

现在截断为您正在使用的位数(这消除了进位位或无限的 1 字符串)。因此,100 是 -4 的 3 位二进制补码表示。

换一种方式,取二进制补码表示 (100) 翻转位 (011) 并添加一个 (100),您现在有了 |-4| 的非二进制补码表示。1 * 2 ^ 2 + 0 * 2 ^ 1 + 0 * 2 ^ 0 = 4。因此我们知道我们开始的表示,100,是-4的3位二进制补码表示。

于 2012-01-18T21:28:50.627 回答
0

这个答案只是一个总结。

在 N 位 2 的补码中:

  • 负范围是 [-2^{N-1}, -1],其基数为 2^{N}/2。
  • 正范围是 [0, 2^{N-1}-1],其基数又是 2^{N}/2。

并且整个范围 [-2^{N-1}, 2^{N-1}-1] 必须具有基数 2^{N}。对超出此范围的任何数字执行 N 位操作将导致溢出。

请注意,当这个有符号范围内的所有数字都加上偏差 2^{N-1} 时,我们得到一个无符号范围 [0, 2^{N-1}]。

于 2022-02-12T09:56:06.237 回答
0

二进制补码通过将最高位保留为负数来表示负数。这意味着您不能再将最高位用作正数。

所有其他(较低)位都是正数,但无论您如何将它们相加,总和永远不会达到最高位,因为它被视为负数。

于 2022-02-12T19:23:11.387 回答