为什么这是一个未定义的行为?

我对这个问题的回答是这个function:

inline bool divisible15(unsigned int x) { //286331153 = (2^32 - 1) / 15 //4008636143 = (2^32) - 286331153 return x * 4008636143 <= 286331153; } 

它完美的工作在我的机器上用VS2008编译器,但是在这里根本不起作用。

有没有人有一个想法,为什么我在不同的编译器上得到不同的结果? unsigned溢出不是未定义的行为。

重要提示:经过一些testing,证实它比15分的剩余部分更快(但不是所有的编译器)

这不是未定义的行为,这只是C89和C99之间C语言标准的突破性变化。

在C89中,整数常量(如4008636143)不适合intlong int但适合在unsigned int中无符号,但在C99中,它们是long intlong long int (取决于哪一个是最小的那一个可以保持价值)。 结果,所有expression式都使用64位进行评估,导致错误的答案。

Visual Studio是C89编译器,因此会导致C89行为,但是Ideone链接正在C99模式下编译。

如果使用-Wall编译GCC,这将变得更加明显:

 test.c: In function 'divisible15': test.c:8:3: warning: this decimal constant is unsigned only in ISO C90 

从C89§3.1.3.2开始:

整数常量的types是其中可以表示其值的对应列表的第一个。 非混色十进制:int,long int,unsigned long int; 未定义的八进制或hex:int,unsigned int,long int,unsigned long int; […]

C99§6.4.4.1/ 5-6:

5)整型常量的types是可以表示其值的对应列表中的第一个。

 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 -------+------------------+------------------------------ [...] 

6)如果一个整数常量不能用列表中的任何types表示,那么它可能有一个扩展整数types,如果扩展整数types可以表示它的值的话。 如果常量列表中的所有types都有符号,则应对扩展的整数types进行签名。 […]

为了完整起见,当整数常量太大而不适合long int时,C ++ 03实际上有未定义的行为。 从C ++ 03§2.13.1/ 2开始:

整数文字的types取决于其forms,值和后缀。 如果它是十进制的,并且没有后缀,则它具有可以表示其值的第一个types: intlong int ; 如果该值不能表示为long int ,则行为是未定义的。 如果它是八进制或hex,并且没有后缀,则它具有可以表示其值的第一个types: intunsigned intlong intunsigned long int 。 […]

C ++ 11的行为与C99相同,请参阅C ++ 11§2.14.2/ 3。

为了确保代码在编译为C89,C99,C ++ 03和C ++ 11时的行为一致,简单的修复方法是使用4008636143u作为后缀,使其不变为4008636143u

由于您使用的是int常数,因此编译器可以“使用”溢出未定义的代码快捷方式。 如果你修改如下U,它“工作”。

 inline bool divisible15(unsigned int x) { //286331153 = (2^32 - 1) / 15 //4008636143 = (2^32) - 286331153 return x * 4008636143u <= 286331153u; } 

testing:

 #include <iostream> inline bool divisible15a(unsigned int x) { //286331153 = (2^32 - 1) / 15 //4008636143 = (2^32) - 286331153 // return x * 4008636143 <= 286331153; return x * 4008636143u <= 286331153; } inline bool divisible15b(unsigned int x) { //286331153 = (2^32 - 1) / 15 //4008636143 = (2^32) - 286331153 // return x * 4008636143 <= 286331153; return x * 4008636143 <= 286331153; } int main() { for(unsigned int i = 0; i < 100; i++) { if (divisible15a(i)) { std::cout << "a:" << i << std::endl; } if (divisible15b(i)) { std::cout << "b:" << i << std::endl; } } } 

输出:

 a:0 b:0 a:15 a:30 a:45 a:60 a:75 a:90 

码:

 _Z12divisible15aj: .LFB1192: pushq %rbp movq %rsp, %rbp movl %edi, -4(%rbp) movl -4(%rbp), %eax imull $-286331153, %eax, %eax cmpl $286331153, %eax setbe %al popq %rbp ret _Z12divisible15bj: .LFB1193: pushq %rbp movq %rsp, %rbp movl %edi, -4(%rbp) movl -4(%rbp), %edx movl $4008636143, %eax imulq %rdx, %rax cmpq $286331153, %rax setle %al popq %rbp ret 

所以,是的,我同意Carl / Adam的分析,认为它不适合32位整数,所以推迟了longlong long