我对this question的回答是这个功能:
inline bool divisible15(unsigned int x)
{
//286331153 = (2^32 - 1) / 15
//4008636143 = (2^32) - 286331153
return x * 4008636143 <= 286331153;
}
使用VS2008编译器完全可以在我的机器上运行,但是here它根本不起作用。
有没有人有想法,为什么我在不同的编译器上得到不同的结果? unsigned
溢出不是未定义的行为。
重要说明:经过一些测试后,确认它比将除法的剩余部分提高15更快。(但不是在所有编译器上)
答案 0 :(得分:98)
这不是未定义的行为,它只是C89和C99之间C语言标准的重大变化。
在C89中,不适合int
或long int
但适合unsigned int
但不适合long int
的整数常量(如4008636143)是无符号的,但在C99中,它们是{{{ 1}}或long long int
(取决于哪一个是可以保存该值的最小值)。结果,所有表达式都使用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:
整数常量的类型是对应的第一个 可以表示其值的列表。未加十进制的:int, long int,unsigned long int; unsuffixed octal或hexadecimal:int, unsigned int,long int,unsigned long int; [...]
C99§6.4.4.1/ 5-6:
5)整数常量的类型是其值可以在其中的第一个相应列表 代表。
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)如果整数常量不能由其列表中的任何类型表示,则它可能具有 扩展整数类型,如果扩展整数类型可以表示其值。如果所有的 常量列表中的类型是有符号的,扩展的整数类型应该是有符号的。 [...]
为了完整性,当整数常量太大而不适合long int
时,C ++ 03实际上确实有未定义的行为。从C ++03§2.13.1/ 2:
整数文字的类型取决于其形式,值和后缀。如果它是十进制的并且没有后缀,则它具有 可以表示其值的第一种类型:
int
,long int
;如果值无法表示 作为long int
,行为未定义。如果它是八进制或十六进制并且没有后缀,则它具有 第一种可以表示其值的类型:int
,unsigned int
,long int
,unsigned long int
。 [...]
C ++ 11的行为与C99相同,参见C ++11§2.14.2/ 3.
为了确保编译为C89,C99,C ++ 03和C ++ 11时代码的行为一致,简单的解决方法是使常量4008636143无符号,后缀为u
为{ {1}}。
答案 1 :(得分:9)
由于您使用int
常量,编译器可以“使用”未定义的溢出来快捷代码。如果你用U修改如下,它“工作”。
inline bool divisible15(unsigned int x)
{
//286331153 = (2^32 - 1) / 15
//4008636143 = (2^32) - 286331153
return x * 4008636143u <= 286331153u;
}
测试:
#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位int,因此提升为long
或long long
。