为啥 0 < -0x80000000?
Posted
技术标签:
【中文标题】为啥 0 < -0x80000000?【英文标题】:Why is 0 < -0x80000000?为什么 0 < -0x80000000? 【发布时间】:2016-03-14 22:53:55 【问题描述】:我有一个简单的程序:
#include <stdio.h>
#define INT32_MIN (-0x80000000)
int main(void)
long long bal = 0;
if(bal < INT32_MIN )
printf("Failed!!!");
else
printf("Success!!!");
return 0;
条件if(bal < INT32_MIN )
始终为真。怎么可能?
如果我将宏更改为:
#define INT32_MIN (-2147483648L)
谁能指出问题?
【问题讨论】:
CHAR_BIT * sizeof(int)
多少钱?
你试过打印出bal吗?
恕我直言,更有趣的是它 only 对于 -0x80000000
,但对于 -0x80000000L
、-2147483648
和 -2147483648L
是错误的(gcc 4.1.2 ),所以问题是:为什么 int 文字 -0x80000000
与 int 文字 -2147483648
不同?
@Bathsheba 我只是在在线编译器上运行程序tutorialspoint.com/codingground.htm
如果你曾经注意到<limits.h>
的(一些化身)将INT_MIN
定义为(-2147483647 - 1)
,现在你知道为什么了。
【参考方案1】:
0x80000000
是一个 unsigned
字面量,值为 2147483648。
对这个仍然应用一元减号会给你一个非零值的无符号类型。 (事实上,对于非零值x
,你最终得到的值是UINT_MAX - x + 1
。)
【讨论】:
【参考方案2】:数字常量0x80000000
的类型为unsigned int
。如果我们取 -0x80000000
并对其进行 2 秒的补码计算,我们会得到:
~0x80000000 = 0x7FFFFFFF
0x7FFFFFFF + 1 = 0x80000000
所以-0x80000000 == 0x80000000
。并且比较(0 < 0x80000000)
(因为0x80000000
是无符号的)是正确的。
【讨论】:
这假定为 32 位int
s。尽管这是一个非常常见的选择,但在任何给定的实现中,int
可能更窄或更宽。然而,对于这种情况,这是一个正确的分析。
这与 OP 的代码无关,-0x80000000
是无符号算术。 ~0x800000000
是不同的代码。
简单地说,这对我来说似乎是最好和正确的答案。 @毫米。他正在解释如何取二进制补码。这个答案专门解决了负号对数字的作用。
@Octopus 负号是 not 对数字应用 2 的补码(!)虽然这看起来很清楚,但它并没有描述代码 -0x80000000
中发生的事情!事实上,2 的补码与这个问题完全无关。【参考方案3】:
这个整数文字0x80000000
的类型为unsigned int
。
根据 C 标准(6.4.4.1 整数常量)
5 整数常量的类型是对应的第一个 可以表示其值的列表。
而这个整数常量可以用unsigned int
的类型来表示。
所以这个表达式
-0x80000000
具有相同的 unsigned int
类型。此外,它具有相同的价值
0x80000000
在二进制补码表示中,计算方式如下
-0x80000000 = ~0x80000000 + 1 => 0x7FFFFFFF + 1 => 0x80000000
这样写例子会有副作用
int x = INT_MIN;
x = abs( x );
结果将再次为INT_MIN
。
因此在这种情况下
bal < INT32_MIN
将0
与unsigned 值0x80000000
根据通常的算术转换规则转换为long long int 类型进行比较。
显然0小于0x80000000
。
【讨论】:
【参考方案4】:这很微妙。
程序中的每个整数文字都有一个类型。 6.4.4.1中的表格规定了它的类型:
Suffix Decimal Constant Octal or Hexadecimal Constant
none int int
long int unsigned int
long long int long int
unsigned long int
long long int
unsigned long long int
如果文字数字不能容纳在默认的int
类型中,它将尝试上表中指示的下一个更大的类型。所以对于常规的十进制整数文字,它就像:
int
如果不合适,试试long
如果不合适,请尝试long long
。
虽然十六进制文字的行为不同! 如果文字不能适合 int
这样的有符号类型,它将首先尝试unsigned int
,然后再尝试更大的类型。请参阅上表中的差异。
因此,在 32 位系统上,您的文字 0x80000000
的类型为 unsigned int
。
这意味着您可以在文字上应用一元 -
运算符,而无需调用实现定义的行为,就像溢出有符号整数时那样。相反,您将获得值0x80000000
,一个正值。
bal < INT32_MIN
调用通常的算术转换,表达式0x80000000
的结果从unsigned int
提升到long long
。 0x80000000
的值被保留,0 小于 0x80000000,因此是结果。
当您用2147483648L
替换文字时,您使用十进制表示法,因此编译器不会选择unsigned int
,而是尝试将其放入long
中。 L 后缀还表示您想要long
如果可能。如果你继续阅读 6.4.4.1 中提到的表格,L 后缀实际上有类似的规则:如果数字不适合所请求的long
,它在 32 位的情况下不适合,编译器会给你long long
非常适合。
【讨论】:
"... 用 -2147483648L 替换文字,您明确得到一个 long,它是有符号的。"嗯,在 32 位long
系统 2147483648L
中,不适合 long
,所以它变成 long long
,然后应用了 -
- 或者我想.
@A.S.H 因为 int 可以拥有的最大数量是 0x7FFFFFFF
。自己试试吧:#include <limits.h> printf("%X\n", INT_MAX);
@A.S.H 不要将源代码中整数文字的十六进制表示与有符号数的底层二进制表示相混淆。以源代码编写的文字 0x7FFFFFFF
始终为正数,但您的 int
变量当然可以包含原始二进制数,最高值为 0xFFFFFFFF。
@A.S.H ìnt n = 0x80000000
强制将无符号文字转换为有符号类型。会发生什么取决于您的编译器 - 它是实现定义的行为。在这种情况下,它选择将整个文字显示到int
中,覆盖符号位。在其他系统上,可能无法表示类型并且您调用未定义的行为 - 程序可能会崩溃。如果您执行int n=2147483648;
,您将获得完全相同的行为,因此它与十六进制表示法完全无关。
一元 -
如何应用于无符号整数的解释可以扩展一点。我一直假设(尽管幸运的是从未依赖于假设)无符号值将被“提升”为有符号值,或者结果可能是未定义的。 (老实说,这应该是一个编译错误;- 3u
是什么意思?)【参考方案5】:
C 有一条规则,整数文字可能是signed
或unsigned
,这取决于它是否适合signed
或unsigned
(整数提升)。在32
-bit 机器上,文字0x80000000
将是unsigned
。在 32 位机器上,-0x80000000
的 2 的补码是 0x80000000
。因此,bal < INT32_MIN
的比较是在signed
和unsigned
之间进行比较,并且在根据C 规则比较之前unsigned int
将转换为long long
。
C11:6.3.1.8/1:
[...] 否则,如果带符号整数类型的操作数的类型可以表示无符号整数类型的操作数的所有值,则将无符号整数类型的操作数转换为带符号整数类型的操作数。
因此,bal < INT32_MIN
始终是 true
。
【讨论】:
【参考方案6】:认为-
是数字常量的一部分时会产生混淆。
在下面的代码中0x80000000
是数字常量。它的类型仅以此确定。 -
是在之后应用的,不会改变类型。
#define INT32_MIN (-0x80000000)
long long bal = 0;
if (bal < INT32_MIN )
Raw 朴素的数字常量是正数。
如果是十进制,则分配的类型是第一个将保存它的类型:int
、long
、long long
。
如果常量是八进制或十六进制,则获取第一个保存它的类型:int
、unsigned
、long
、unsigned long
、long long
、unsigned long long
。
0x80000000
,在 OP 的系统上获取unsigned
或unsigned long
的类型。无论哪种方式,它都是某种无符号类型。
-0x80000000
也是一些非零值并且是一些无符号类型,它大于 0。当代码将其与 long long
进行比较时,值不会在 2比较的两边,所以0 < INT32_MIN
是真的。
另一种定义避免了这种奇怪的行为
#define INT32_MIN (-2147483647 - 1)
让我们在int
和unsigned
都是48 位的幻想世界里走一走吧。
然后0x80000000
适合int
,类型int
也是如此。 -0x80000000
则为负数,打印出来的结果不同。
[回到现实]
由于0x80000000
在有符号类型之前适合某些无符号类型,因为它刚好比some_signed_MAX
大,但在some_unsigned_MAX
之内,它是一些无符号类型。
【讨论】:
以上是关于为啥 0 < -0x80000000?的主要内容,如果未能解决你的问题,请参考以下文章
为啥 `j = +i + ( i < 0 ? len : 0 )` 中的前导 `+` (取自 jQuery 源代码)[重复]