我还没有找到一个原因,为什么最低的有符号负数没有对应的有符号正数呢?
我的意思是在一个三位二进制数中,简单起见。
100是-4吗?但我们不能用有符号格式表示正4,因为它会溢出。那么我们怎么知道二进制补码1000是-4,10000000是-128等等呢?我们没有原始的正数。
我还没有找到一个原因,为什么最低的有符号负数没有对应的有符号正数呢?
我的意思是在一个三位二进制数中,简单起见。
100是-4吗?但我们不能用有符号格式表示正4,因为它会溢出。那么我们怎么知道二进制补码1000是-4,10000000是-128等等呢?我们没有原始的正数。
有一种思考方式是将带符号的二进制采用二补码表示,具体方法是给每个比特位分配一个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解释为有符号的三位二进制补码值时,它回到了与我们开始时相同的状态。位数不足是问题所在。-INT_MIN
是"未定义行为",编译器可能会忽略上述所有答案。在我看来,这是一个缺失的重要部分。 - chux - Reinstate Monica-INT_MIN
是在 C 语言中的整数溢出,属于未定义行为。
只有当带符号整数溢出时才能保证 -INT_MIN
等于 INT_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
是未定义行为这一事实。
-fwrapv
是正确的标志,可以确保有符号整数进行模运算时会发生环绕。-fno-strict-overflow
防止优化器假设整数溢出永远不会发生。 - nucleon-fwrapv
是正确的选项,在这里,谢谢。我已经编辑了我的答案。 - ouahA. n位二进制数有偶数种可能性,因此我们无法用相同的方法表示正数和负数的范围。
B. 我们希望以1开头的所有数字都是负数,以0开头的所有数字都不是负数。(相反的情况不行,因为我们希望在有符号和无符号的情况下,正数和零具有相同的表现形式。因此0位于正数的一半,它们就少了一位。)
与二进制补码相反的是一补数,它具有这样的属性。
在一补数形式中,最小可能值也有一个有效的正形式。
0110 == 6
,在一的补码中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
。从数学角度来看,0既不是正数也不是负数。但在二进制中,由于0
没有负位,因此被视为正数。换句话说,如果你想要-128到128之间的数字,就不能有0。因为您必须将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的幂)。
这个答案只是一个总结。
在N位2的补码中:
而整个范围[-2^{N-1}, 2^{N-1}-1]必须具有基数2^{N}。对此范围内的任何数字执行N位操作都会导致溢出。
请注意,当该有符号范围中的所有数字添加偏差2^{N-1}时,我们得到一个无符号范围[0, 2^{N-1}]。
二进制补码通过将最高位保留为负数来表示负数。这意味着您不能再将最高位用作正数。
所有其他(较低的)位都是正数,但无论如何将它们相加,总和永远无法达到最高位,因为它被视为负数。
要反着来,取二进制补码表示(100),翻转位(011)并加一(100),你现在有非二进制补码表示形式的|-4|。1*2^2 + 0*2^1 + 0*2^0 = 4。因此我们知道我们最初开始的表示形式,即100,是-4的3位二进制补码表示。
所以 INT_MIN = -INT_MIN