I've written this C code for finding the sum of all integers which are equal to the sum of the factorial of their digits. It takes a minute or so to get the job done without any GCC optimization flags, using -O1 decreased that time by about 15-20 seconds but when I tried it with -O2, -O3 or -Os it gets stuck in an infinite loop.
int main()
{
int i, j, factorials[10];
int Result=0;
for(i=0; i<10; i++)
{
factorials[i]=1;
for(j=i; j>0; j--)
{
factorials[i] *= j;
}
}
for(i=3; i>2; i++) //This is the loop the program gets stuck on
{
int Sum=0, number=i;
while(number)
{
Sum += factorials[number % 10];
number /= 10;
}
if(Sum == i)
Result += Sum;
}
printf("%d\n", Result);
return 0;
}
I've pinpointed that for(i=3; i>2; i++)
is the cause of the problem. So obviously i
is never less than 2?
Does this have anything to do with the fact that integer overflow behavior is undefined? If so, any more info on what exactly is going on with the program in these cases?
EDIT: I guess I should've mentioned, I am aware of other ways of writing that for loop so that it doesn't use overflowing(I was hoping that INT_MAX+1 would be equal to INT_MIN which is <2) but this was just done as a random test to see what would happen and I posted it here to find out what exactly was going on :)
The loop is for (i = 3; i > 2; i++)
and it has no break
statements or other exit condition.
Eventually i
will reach INT_MAX
and then i++
will cause integer overflow which causes undefined behaviour.
Possibly Sum
or Result
would also overflow before i
did.
When a program is guaranteed to trigger undefined behaviour , the entire behaviour of the program is undefined.
gcc is well known for aggressively optimizing out paths that trigger UB . You could inspect the assembly code to see what exactly happened in your case. Perhaps the -O2
and higher cases removed the loop end condition check , but -O1
left it in there and "relied" on INT_MAX + 1
resulting in INT_MIN
.
The for loop is for(i=3; i>2; i++)
and inside this loop i
is not modified, nor is there a break
or any other way to exit the loop. You are relying on integer overflow to cause the exit condition to occur, but the compiler doesn't take that into consideration.
Instead, the compiler sees that i
starts at 3, and i
is only ever incremented, and so i>2
is always true. Thus there is no need for i
to exist at all in this context, since this must be an infinite loop.
If you change i
to be unsigned int
and set the condition for the loop exit to match, this "optimization" will no longer occur.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With