(-2147483648> 0)在C ++中返回true吗?


241

-2147483648是32位整数类型的最小整数,但似乎会在if(...)句子中溢出:

if (-2147483648 > 0)
    std::cout << "true";
else
    std::cout << "false";

这将true在我的测试中打印。但是,如果将-2147483648强制转换为整数,结果将有所不同:

if (int(-2147483648) > 0)
    std::cout << "true";
else
    std::cout << "false";

这将打印false

我很困惑。谁能解释一下?


更新02-05-2012:

感谢您的评论,在我的编译器中,int的大小为4个字节。我正在使用VC进行一些简单的测试。我已经更改了问题的描述。

这在这篇文章中得到了很多很好的答复,AndreyT给出了关于编译器将如何对此类输入进行操作以及如何实现此最小整数的非常详细的解释。另一方面,qPCR4vir提供了一些相关的“好奇心”以及如何表示整数。好厉害!


48
“我们都知道-2147483648是整数的最小数目”,这取决于整数的大小。
orlp

14
“我们都知道-2147483648是最小的整数”-我认为没有最小的整数,因为其中无限多。

@Inisheer如果使用4字节整数,则可能具有INT_MINof -9223372036854775808,如果CHAR_BIT为16。即使使用CHAR_BIT == 8and sizeof(int== 4),也可能会得到,-9223372036854775807因为C不需要2补码。
12431234123412341234123

Answers:


391

-2147483648不是“数字”。C ++语言不支持负文字值。

-2147483648实际上是一个表达式:在其前面2147483648带有一元运算-符的正文字值。2147483648对于int平台范围内的积极方面,价值显然太大。如果类型long int有你的平台上更大的范围,编译器将不得不自动假设2147483648long int型。(在C ++ 11中,编译器还必须考虑long long int类型。)这将使编译器-2147483648在较大类型的域中求值,并且结果将为负数,这与预期的一样。

但是,显然在您的情况下,范围与的范围long int相同int,并且通常不存在比int平台更大的整数类型。正式地,这意味着正常数会2147483648溢出所有可用的带符号整数类型,这又意味着程序的行为是不确定的。(在这种情况下语言规范选择了未定义的行为,而不是要求诊断消息,这有点奇怪,但这就是事实。)

在实践中,考虑到行为是不确定的,2147483648可能会被解释为某些依赖于实现的负值,在-对它应用一元后,它会变成正值。或者,某些实现可能会决定尝试使用无符号类型来表示值(例如,在C89 / 90中要求使用编译器unsigned long int,而在C99或C ++中则不需要)。允许执行任何操作,因为无论如何行为都是未定义的。

顺便说一句,这就是为什么INT_MIN通常将like 定义为的原因

#define INT_MIN (-2147483647 - 1)

而不是看似更直接

#define INT_MIN -2147483648

后者将无法按预期工作。


78
这也是这样做的原因:#define INT_MIN (-2147483647 - 1)
orlp

5
@ RichardJ.RossIII-使用clang,您可能会得到64位类型的文字,因为它太大而无法放入int。OP的实现可能没有64位类型。
卡尔·诺鲁姆

1
@ RichardJ.RossIII:我相信这种行为是实现定义的/未定义的。
奥利弗·查尔斯沃思

3
我从没想过没有这样解析过“负数”。我没看到原因。我希望将-1.0其解析为负双精度值,不是吗?
leemes

6
@ qPCR4vir:否。正如我在对您的答案的评论中所写,在这种情况下,现代C或C ++都不允许使用无符号类型(带有无后缀的十进制常量)。unsigned long int在这种情况下,仅允许使用第一个标准C(C89 / 90),但在C99中,此许可被删除。C和C ++中不带后缀的文字必须具有带符号的类型。如果您在这里看到无符号类型,那么有签名的类型将起作用,这意味着您的编译器已损坏。如果在没有符号类型无法工作的情况下在此处看到无符号类型,则这只是未定义行为的一种具体表现。
AnT

43

编译器(VC2012)提升为可以容纳值的“最小”整数。在第一种情况下,signed int(和long int)不能(在应用符号之前),但是unsigned int可以:2147483648具有unsigned int ???? 类型。在第二个中,您int从强制unsigned

const bool i= (-2147483648 > 0) ;  //   --> true

警告C4146:一元减运算符应用于无符号类型,结果仍为无符号

以下是相关的“好奇心”:

const bool b= (-2147483647      > 0) ; //  false
const bool i= (-2147483648      > 0) ; //  true : result still unsigned
const bool c= ( INT_MIN-1       > 0) ; //  true :'-' int constant overflow
const bool f= ( 2147483647      > 0) ; //  true
const bool g= ( 2147483648      > 0) ; //  true
const bool d= ( INT_MAX+1       > 0) ; //  false:'+' int constant overflow
const bool j= ( int(-2147483648)> 0) ; //  false : 
const bool h= ( int(2147483648) > 0) ; //  false
const bool m= (-2147483648L     > 0) ; //  true 
const bool o= (-2147483648LL    > 0) ; //  false

C ++ 11标准

2.14.2整数文字[lex.icon]

整数文字是没有句号或指数部分的数字序列。整数文字可以具有指定其基数的前缀和指定其类型的后缀。

整数文字的类型是对应列表的第一个,可以在其中表示其值。

在此处输入图片说明

如果整数文字不能用其列表中的任何类型表示,并且扩展整数类型(3.9.1)可以表示其值,则它可能具有该扩展整数类型。如果文字列表中的所有类型均已签名,则扩展整数类型应被签名。如果文字列表中的所有类型都是无符号的,则扩展整数类型应为无符号的。如果列表同时包含有符号和无符号类型,则扩展整数类型可以是有符号或无符号的。如果程序的翻译单元之一包含不能用任何允许的类型表示的整数文字,则该程序格式错误。

这些是标准中整数的促销规则。

4.5整体促销 [conv.prom]

以外的整数类型的prvalue boolchar16_tchar32_t,或 wchar_t,其整数转换秩(4.13)小于INT的秩可以被转换成类型的prvalue int如果int可以表示源类型的所有值; 否则,可以将源prvalue转换为type的prvalue unsigned int


3
@ qPCR4vir:在C89 / 90的编译器本应该利用类型intlong intunsigned long int表示无后缀十进制常数。那是唯一一种允许对无后缀的十进制常量使用无符号类型的语言。在C ++ 98中,它是intlong int。不允许使用无符号类型。C(从C99开始)或C ++都不允许编译器在这种情况下使用无符号类型。当然,如果没有签名类型,编译器可以自由使用无符号类型,但这仍然只是未定义行为的一种具体表现。
AnT

@AndreyT。大!当然,你的严厉。VC2012是否损坏?
qPCR4vir

@ qPCR4vir:AFAIK,VC2012还不是C ++ 11编译器(是吗?),这意味着它必须使用intlong int表示2147483648。此外,AFAIK,在VC2012两者intlong int是32位的类型。这意味着在VC2012中,文字2147483648应导致未定义的行为。当行为未定义时,允许编译器执行任何操作。这意味着VC2012不会损坏。它只是发出了误导性的诊断消息。它没有告诉您行为是完全未定义的,而是决定使用无符号类型。
AnT

@AndreyT:您是说,如果源代码包含不带后缀的十进制文字(其值超过signed的最大值)long并且不需要发出诊断,则编译器可以自由发出鼻恶魔?那似乎坏了。
2013年

VS2008中相同的“警告C4146”和G ++中的“此十进制常数仅在ISO C90中是无符号的”
spyder 2013年

6

简而言之,2147483648溢出到-2147483648,并且(-(-2147483648) > 0)true

2147483648二进制形式。

另外,在带符号的二进制计算中,最高有效位(“ MSB”)是符号位。这个问题可能有助于解释原因。


4

由于-2147483648实际上对其应用2147483648了否定(-),因此该数字不是您所期望的。它实际上等效于此伪代码:operator -(2147483648)

现在,假设您的编译器具有sizeof(int)4并且CHAR_BIT被定义为8,这将使2147483648溢出成为整数(2147483647)的最大有符号值。那么最大加一是多少?让我们用一个4位2s的补码整数来计算。

等待!8溢出整数!我们做什么?使用其无符号表示形式1000并将这些位解释为有符号整数。这种表示使我们得以-8应用2s补码求反,导致8,众所周知,它大于0

这就是为什么<limits.h>(和<climits>)通常定义INT_MIN((-2147483647) - 1)-,从而使最大有符号整数(0x7FFFFFFF)取反(0x80000001),然后递减(0x80000000)。


对于4位数字,的补码取反-8仍为-8
Ben Voigt

除了-8被解释为0-8而不是负8之外,而8溢出了一个4位有符号整数
Cole Johnson

考虑一下-(8)C ++中的哪个与-8-是否定应用于文字,而不是否定文字。文字是8,它不适合带符号的4位整数,因此必须为无符号。模式是1000。到目前为止,您的答案是正确的。10004位的二进制补码取反为1000,无论是带符号还是无符号都无所谓。您的答案是说“将位解释为有符号整数”,该值使-8补数取反之后的值与取反之前的值相同。
Ben Voigt

当然,在“ 4位C ++”中,没有“将位解释为有符号整数步”。文字变成可以表达它的最小类型,它是无符号的4位整数。文字的值是8。施加否定(模16),最终答案为8。编码仍然是1000,但是值有所不同,因为选择了无符号类型。
Ben Voigt
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.