在Leetcode的Pow(x, n)中,有一组特殊的测试数据,即1.0 -2147483648
(求1.0^-2147483648)。同时我也发现Stack Overflow上一个与之类似的问题和答案。提问和回复都很精彩,就着手翻译一下(其实是为了加深记忆)。
问:
#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)
后,程序正常工作。
答:
这很难察觉。
每一个整数字面值在你的程序中都有一个类型。这些类型有如下标准,如表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
类型中,它将试图采用下一个类型(如表6.4.4.1)所示。所以对于十进制整数字面值,将进行如下过程:
- 尝试
int
- 如果不能存放,尝试
long
- 如果还不能存放,尝试
long long
而十六进制的字面值的行为与上述过程大不相同。如果字面值不能存放在一个有符号类型如int
中,在它提升类型前会首先将尝试存放在unsigned int
。我们可以从表6.4.4.1中看出其中的差异。
所以在一个32位操作系统中,你的字面值是 0x80000000
是一个 unsigned int
类型。
这意味着你对它使用一元操作符-
将不能达到相应的效果( 一元负运算符应用于无符号类型,结果仍为无符号类型)。另一种情况是它会造成一个有符号整数的溢出。相反的,你将得到值0x80000000
,一个正值。
bal < INT32_MIN
将执行常见的算数类型转换,表达式中的0x80000000
将从unsigned int
转换为long long
类型存放,0小于0x80000000
,因此产生了如此结果。
当你使用十进制形式将字面值替换为2147483648L
后,编译器将不再选择unsigned int
, 而是尝试将它存放在一个long
类型中。还有后缀L
意味着如果可能的话,你想用一个long
类型来进行存放。后缀L
通常遵守相同的规则(如表6.4.4.1):如果这个数不能存放在它所请求的long
类型中,编译器将会给你一个long long
类型,直到它可以完成正确的存放。