249

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

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

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

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

这将打印false.

我很困惑。任何人都可以对此作出解释吗?


2012 年 2 月 5 日更新:

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

在这篇文章中有很多非常好的回复,AndreyT非常详细地解释了编译器将如何处理此类输入,以及如何实现这个最小整数。另一方面, qPCR4vir给出了一些相关的“好奇心”以及整数的表示方式。如此令人印象深刻!

4

4 回答 4

399

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

-2147483648实际上是一个表达式:一个正的文字值,前面2147483648带有一元运算符。对于您平台上范围的积极方面来说,-价值2147483648显然太大了。int如果类型long int在您的平台上有更大的范围,编译器将不得不自动假定它2147483648具有long int类型。(在 C++11 中,编译器还必须考虑long long int类型。)这将使编译器-2147483648在更大类型的域中进行评估,结果将是负数,正如人们所期望的那样。

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

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

作为旁注,这就是为什么像这样的常量INT_MIN通常被定义为

#define INT_MIN (-2147483647 - 1)

而不是看似更直接的

#define INT_MIN -2147483648

后者不会按预期工作。

于 2013-02-04T20:38:46.733 回答
43

编译器(VC2012)提升为可以保存值的“最小”整数。在第一种情况下,signed int(and long int) 不能(在应用符号之前),但是unsigned int可以:2147483648unsigned int???? 类型。在第二个中,您intunsigned.

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]

一个除bool, char16_t,之外的整数类型的纯右值char32_t,或者 wchar_t其整数转换等级 (4.13) 小于 int 等级的纯右值,int如果int可以表示源类型的所有值,则可以转换为类型的纯右值;否则,源纯右值可以转换为类型的纯右值unsigned int

于 2013-02-04T20:50:19.903 回答
5

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

就是2147483648二进制的样子。

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

于 2013-02-05T02:42:28.373 回答
4

因为-2147483648实际上是对它应用2147483648了否定 ( -),所以这个数字不是你所期望的。它实际上相当于这个伪代码:operator -(2147483648)

现在,假设您的编译器sizeof(int)等于4CHAR_BIT定义为8,这将使溢出成为整数 ( )2147483648的最大有符号值。2147483647那么最大加一是多少呢?让我们用一个 4 位、2 秒的补码整数来解决这个问题。

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

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

于 2013-02-05T18:21:40.420 回答