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

ben*_*nyl 238 c++ overflow

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

if (-2147483648 > 0)
    std::cout << "true";
else
    std::cout << "false";
Run Code Online (Sandbox Code Playgroud)

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

if (int(-2147483648) > 0)
    std::cout << "true";
else
    std::cout << "false";
Run Code Online (Sandbox Code Playgroud)

这将打印false.

我糊涂了.谁能对此作出解释?


更新02-05-2012:

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

这篇文章中有很多非常好的回复,AndreyT对编译器在这样的输入上的行为以及如何实现这个最小整数给出了非常详细的解释.另一方面,qPCR4vir给出了一些相关的"好奇心"以及如何表示整数.太棒了!

AnT*_*AnT 387

-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)
Run Code Online (Sandbox Code Playgroud)

而不是看似更直接的

#define INT_MIN -2147483648
Run Code Online (Sandbox Code Playgroud)

后者无法按预期工作.

  • 这也是为什么这样做的原因:`#define INT_MIN(-2147483647 - 1)`. (78认同)
  • @ qPCR4vir:不.正如我在你的评论中写的那样,现代C和C++都不允许在这种情况下使用无符号类型(带有*unsuffixed十进制常量*).在此上下文中,只有第一个标准C(C89/90)允许`unsigned long int`,但在C99中,此权限已被删除.C和C++中的非语法文字需要具有*signed*类型.如果您在签名的工作时看到未签名类型,则表示您的编译器已损坏.如果您在没有签名类型的情况下看到未签名类型,那么这只是未定义行为的特定表现形式. (6认同)
  • @ RichardJ.RossIII - 有了clang你可能会得到一个64位类型的文字,因为它太大而不适合`int`.OP的实现可能没有64位类型. (5认同)
  • 我从未想过"负数"不会被解析.我没有看到原因.我希望`-1.0`被解析为负双值,不是吗? (3认同)

qPC*_*vir 43

编译器(VC2012)提升到可以保存值的"最小"整数.在第一种情况下,signed int(和long int)不能(在符号应用之前),但unsigned int可以:2147483648hasunsigned int ???? 类型.在第二个你迫使intunsigned.

const bool i= (-2147483648 > 0) ;  //   --> true
Run Code Online (Sandbox Code Playgroud)

警告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
Run Code Online (Sandbox Code Playgroud)

C++ 11标准:

2.14.2整数文字[lex.icon]

...

整数文字是一系列没有句点或指数部分的数字.整数文字可以具有指定其基数的前缀和指定其类型的后缀.

...

整数文字的类型是相应列表中可以表示其值的第一个.

在此输入图像描述

如果整数文字不能由其列表中的任何类型表示,并且扩展整数类型(3.9.1)可以表示其值,则它可能具有该扩展整数类型.如果文字列表中的所有类型都已签名,则应对扩展整数类型进行签名.如果文字列表中的所有类型都是无符号的,则扩展整数类型应为无符号.如果列表包含有符号和无符号类型,则扩展整数类型可以是有符号或无符号的.如果某个程序的翻译单元中包含一个无法用任何允许类型表示的整数文字,则该程序格式不正确.

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

4.5整体促销 [conv.prom]

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

  • @ qPCR4vir:在C89/90中,编译器应该使用类型`int`,`long int`,`unsigned long int`来表示未加十进制的十进制常量.这是唯一允许使用无符号类型的非语义十进制常量的语言.在C++ 98中,它是`int`或`long int`.不允许使用无符号类型.C(从C99开始)和C++都不允许编译器在此上下文中使用无符号类型.当然,如果没有签名的编译工作,你的编译器可以自由使用无符号类型,但这仍然是未定义行为的特定表现. (3认同)

drz*_*ala 7

简而言之,2147483648溢出-2147483648,(-(-2147483648) > 0)true.

2147483648二进制的样子.

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


Col*_*son 6

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

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

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

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