My answer to this question was this function:
inline bool divisible15(unsigned int x)
{
//286331153 = (2^32 - 1) / 15
//4008636143 = (2^32) - 286331153
return x * 4008636143 <= 286331153;
}
It perfectly worked on my machine with VS2008 compiler, however here it doesn't work at all.
Does anyone has an idea, why it I get different results on different compilers? unsigned
overflow isn't undefined behavior.
Important note: after some test it was confirmed it is faster than taking the remainder of the division by 15. (However not on all compilers)
It's not Undefined Behavior, it's just a breaking change in the C language standard between C89 and C99.
In C89, integer constants like 4008636143 that don't fit in an
int
orlong int
but do fit in anunsigned int
are unsigned, but in C99, they are eitherlong int
orlong long int
(depending on whichever one is the smallest one that can hold the value). As a result, the expressions all get evaluated using 64 bits, which results in the incorrect answer.Visual Studio is a C89 compiler and so results in the C89 behavior, but that Ideone link is compiling in C99 mode.
This becomes more evident if you compile with GCC using
-Wall
:From C89 §3.1.3.2:
C99 §6.4.4.1/5-6:
For completeness, C++03 actually does have Undefined Behavior for when the integer constant is too big to fit in a
long int
. From C++03 §2.13.1/2:The C++11 behavior is identical to C99, see C++11 §2.14.2/3.
To ensure that the code behaves consistently when compiled as either C89, C99, C++03, and C++11, the simple fix is to make the constant 4008636143 unsigned by suffixing it with
u
as4008636143u
.Since you are using
int
constants, the compiler can "use" the overflow is undefined to shortcut the code. If you modify with U as below, it "works".testing with:
Output:
Code:
So, yes, I agree with Carl/Adam's analysis that it doesn't fit in a 32-bit int, so therefore promoted to
long
orlong long
.