g++ optimization breaks for loops - c++

A few days ago, I encountered what I believe to be a bug in g++ 5.3 concerning the nesting of for loops at higher -OX optimization levels. (Been experiencing it specifically for -O2 and -O3). The issue is that if you have two nested for loops, that have some internal sum to keep track of total iterations, once this sum exceeds its maximum value it prevents the outer loop from terminating. The smallest code set that I have been able to replicate this with is:
int main(){
int sum = 0;
// Value of 100 million. (2047483648 less than int32 max.)
int maxInner = 100000000;
int maxOuter = 30;
// 100million * 30 = 3 billion. (Larger than int32 max)
for(int i = 0; i < maxOuter; ++i)
{
for(int j = 0; j < maxInner; ++j)
{
++sum;
}
std::cout<<"i = "<<i<<" sum = "<<sum<<std::endl;
}
}
When this is compiled using g++ -o run.me main.cpp it runs just as expected outputting:
i = 0 sum = 100000000
i = 1 sum = 200000000
i = 2 sum = 300000000
i = 3 sum = 400000000
i = 4 sum = 500000000
i = 5 sum = 600000000
i = 6 sum = 700000000
i = 7 sum = 800000000
i = 8 sum = 900000000
i = 9 sum = 1000000000
i = 10 sum = 1100000000
i = 11 sum = 1200000000
i = 12 sum = 1300000000
i = 13 sum = 1400000000
i = 14 sum = 1500000000
i = 15 sum = 1600000000
i = 16 sum = 1700000000
i = 17 sum = 1800000000
i = 18 sum = 1900000000
i = 19 sum = 2000000000
i = 20 sum = 2100000000
i = 21 sum = -2094967296
i = 22 sum = -1994967296
i = 23 sum = -1894967296
i = 24 sum = -1794967296
i = 25 sum = -1694967296
i = 26 sum = -1594967296
i = 27 sum = -1494967296
i = 28 sum = -1394967296
i = 29 sum = -1294967296
However, when this is compiled using g++ -O2 -o run.me main.cpp, the outer loop fails to terminate. (This only occurs when maxInner * maxOuter > 2^31) While sum continually overflows, it shouldn't in any way affect the other variables. I have also tested this on Ideone.com with the test case demonstrated here: https://ideone.com/5MI5Jb
My question is thus twofold.
How is it possible for the value of sum to in some way effect the system? No decisions are based upon its value, it is merely utilized for the purposes of a counter and the std::cout statement.
What could possibly be causing the dramatically different outcomes at different optimization levels?
Thank you greatly in advance for taking the time to read and consider my question.
Note: This question differs from existing questions such as: Why does integer overflow on x86 with GCC cause an infinite loop? because the issue with that problem was an overflow for the sentinal variable. However, both sentinal variables in this question i and j never exceed the value of 100m let alone 2^31.

This is an optimisation that's perfectly valid for correct code. Your code isn't correct.
What GCC sees is that the only way the loop exit condition i >= maxOuter could ever be reached is if you have signed integer overflow during earlier loop iterations in your calculation of sum. The compiler assumes there isn't signed integer overflow, because signed integer overflow isn't allowed in standard C. Therefore, i < maxOuter can be optimised to just true.
This is controlled by the -faggressive-loop-optimizations flag. You should be able to get the behaviour you expect by adding -fno-aggressive-loop-optimizations to your command line arguments. But better would be making sure your code is valid. Use unsigned integer types to get guaranteed valid wraparound behaviour.

Your code invokes undefined behaviour, since the int sum overflows. You say "this shouldn't in any way affect the other variables". Wrong. Once you have undefined behaviour, all odds are off. Anything can happen.
gcc is (in)famous for optimisations that assume there is no undefined behaviour and do let's say interesting things if undefined behaviour happens.
Solution: Don't do it.

Answers
As #hvd pointed out, the problem is in your invalid code, not in the compiler.
During your program execution, the sum value overflows int range. Since int is by default signed and overflow of signed values causes undefined behavior* in C, the compiler is free to do anything. As someone noted somewhere, dragons could be flying out of your nose. The result is just undefined.
The difference -O2 causes is in testing the end condition. When the compiler optimizes your loop, it realizes that it can optimize away the inner loop, making it
int sum = 0;
for(int i = 0; i < maxOuter; i++) {
sum += maxInner;
std::cout<<"i = "<<i<<" sum = "<<sum<<std::endl;
}
and it may go further, transforming it to
int i = 0;
for(int sum = 0; sum < (maxInner * maxOuter); sum += maxInner) {
i++;
std::cout<<"i = "<<i<<" sum = "<<sum<<std::endl;
}
To be honest, I don't really know what it does, the point is, it can do just this. Or anything else, remember the dragons, your program causes undefined behavior.
Suddenly, your sum variable is used in the loop end condition. Note that for defined behavior, these optimizations are perfectly valid. If your sum was unsigned (and your maxInner and maxOuter), the (maxInner * maxOuter) value (which would also be unsigned) would be reached after maxOuter loops, because unsigned operations are defined** to overflow as expected.
Now since we're in the signed domain, the compiler is for one free to assume, that at all times sum < (maxInner * maxOuter), just because the latter overflows, and therefore is not defined. So the optimizing compiler can end up with something like
int i = 0;
for(int sum = 0;/* nothing here evaluates to true */; sum += maxInner) {
i++;
std::cout<<"i = "<<i<<" sum = "<<sum<<std::endl;
}
which looks like observed behavior.
*: According to the C11 standard draft, section 6.5 Expressions:
If an exceptional condition occurs during the evaluation of an expression (that is, if the result is not mathematically defined or not in the range of representable values for its type), the behavior is undefined.
**: According to the C11 standard draft, Annex H, H.2.2:
C’s unsigned integer types are ‘‘modulo’’ in the LIA−1 sense in that overflows or out-of-bounds results silently wrap.
I did some research on the topic. I compiled the code above with gcc and g++ (version 5.3.0 on Manjaro) and got some pretty interesting things of it.
Description
To successfully compile it with gcc (C compiler, that is), I have replaced
#include <iostream>
...
std::cout<<"i = "<<i<<" sum = "<<sum<<std::endl;
with
#include <stdio.h>
...
printf("i = %d sum = %d\n", i, sum);
and wrapped this replacement with #ifndef ORIG, so I could have both versions. Then I ran 8 compilations: {gcc,g++} x {-O2, ""} x {-DORIG=1,""}. This yields following results:
Results
gcc, -O2, -DORIG=1: Won't compile, missing <iostream>. Not surprising.
gcc, -O2, "": Produces compiler warning and behaves "normally". A look in the assembly shows that the inner loop is optimized out (j being incremented by 100000000) and the outer loop variable is compared with hardcoded value -1294967296. So, GCC can detect this and do some clever things while the program is working expectably. More importantly, warning is emitted to warn user about undefined behavior.
gcc, "", -DORIG=1: Won't compile, missing <iostream>. Not surprising.
gcc, "", "": Compiles without warning. No optimizations, program runs as expected.
g++, -O2, -DORIG=1: Compiles without warning, runs in endless loop. This is OP's original code running. C++ assembly is tough to follow for me. Addition of 100000000 is there though.
g++, -O2, "": Compiles with warning. It is enough to change how the output is printed to change compiler warning emiting. Runs "normally". By the assembly, AFAIK the inner loop gets optimized out. At least there is again comparison against -1294967296 and incrementation by 100000000.
g++, "", -DORIG=1: Compiles without warning. No optimization, runs "normally".
g++, "", "": dtto
The most interesting part for me was to find out the difference upon change of printing. Actually from all the combinations, only the one used by OP produces endless-loop program, the others fail to compile, do not optimize or optimize with warning and preserve sanity.
Code
Follows example build command and my full code
$ gcc -x c -Wall -Wextra -O2 -DORIG=1 -o gcc_opt_orig main.cpp
main.cpp:
#ifdef ORIG
#include <iostream>
#else
#include <stdio.h>
#endif
int main(){
int sum = 0;
// Value of 100 million. (2047483648 less than int32 max.)
int maxInner = 100000000;
int maxOuter = 30;
// 100million * 30 = 3 billion. (Larger than int32 max)
for(int i = 0; i < maxOuter; ++i)
{
for(int j = 0; j < maxInner; ++j)
{
++sum;
}
#ifdef ORIG
std::cout<<"i = "<<i<<" sum = "<<sum<<std::endl;
#else
printf("i = %d sum = %d\n", i, sum);
#endif
}
}

Related

CodeSignal isLucky task c++

Started to practice C++ by trying to do some tasks in CodeSignal, but I can’t figure it out why it has an output which is always false. Saw a similar answer to this task but didn’t want to copy and paste without understanding where the error is.
Ticket numbers usually consist of an even number of digits. A ticket number is considered lucky if the sum of the first half of the digits is equal to the sum of the second half.
Example:
For n = 1230, the output should be isLucky(n) = true
For n = 239017, the output should be isLucky(n) = false
Code:
bool isLucky(int n) {
string convert = to_string(n); // to convert from string to int
int sizehalbe = convert.size() / 2; //divide into 2 halfs
//Stor each half
string h1 = convert.substr(0, sizehalbe-1);
string h2 = convert.substr(sizehalbe, convert.size()-1);
int sum1=0, sum2=0; //Calculate the halfs
for(int i=0;i<h1.length();i++)
{
sum1 += int(h1.at(i));
}
for(int j=0;j<h2.length();j++)
{
sum2 += int(h2.at(j));
}
if(sum1 == sum2)
return true;
else
return false;
}
(1). Foremost your h1 always miss one digit so instead of
h1 = convert.substr(0, sizehalbe-1);
that's the only main issue your code has, convert should be gone till sizehalbe
string h1 = convert.substr(0, sizehalbe);
(2). whenever you typecast from character to integer, check what it gives
cout<<int('0'); will give you 48 instead of 0.
in particular, this case it's not changed your main output
(due to both sum1 & sum2 will get higher result than what actually should be,
but get same level of higher.)
sum½ += int(h½.at(i)) - 48;
(3). you can optimize your last condition.
when boolean result is depending on condition you can do
return (sum1 == sum2);

Why is this loop being endless?

I have been programming for a long time and stumbled across something very weird.
Somewhere in my code I had the following code:
for(int i = 0; i < 512; i++){
if((i * 12391823) % 5 == 1){
std::cout << i << std::endl;
}
}
I have already tracked the problem down to this piece of code.
I am using CLion. When compiling a Debug build, the loop is not endless and finishes eventually after printing a few numbers.
Yet when building as Release, it seems to never exit the loop.
...
>>> 15968768
>>> 15968773
>>> 15968778
...
If (i * 12391823) is replaced with a different (smaller) number, this does not happen.
E.g. with (i * 123), it does exit nicely:
...
>>> 482
>>> 487
>>> 492
...
I have also looked at the compile output which displayed the following:
warning: iteration 174 invokes undefined behavior [-Waggressive-loop-optimizations]
16 | if((i * 12391823) % 5 == 1){
| ~~~^~~~~~~~~~~
I do wonder why this would lead to the loop not ending.
It seems to overflow, yet it is not supposed to change i and therefor end the loop at some point, right?
I am very happy for an explanation on this topic!
Greetings
FInn
I do wonder why this would lead to the loop not ending. It seems to overflow, yet it is not supposed to change i and therefor end the loop at some point, right?
The answer is "Undefined Behaviour is undefined". It can do anything. But, the fact that you get a warning from aggressive-loop-optimizations may hint at the reason why loop becomes endless. It is possible that compiler decides to modify your loop into
for(int i = 0; i < 512 * 12391823; i + 12391823){
if(i % 5 == 1){
std::cout << i << std::endl;
}
}
Or maybe even
for(int i = 24783646; i < 512 * 12391823; i + 12391823 * 5){
std::cout << i << std::endl;
}
Both options could act strangely when integer overflow happens.
The solution is to not get into Undefined Behaviour lands. You can do that for example by changing type of i from int to unsigned long long
for(unsigned long long i = 0; i < 512; i++)
You are hitting undefined behavior in the line:
if((i * 12391823) % 5 == 1){
This is because for i larger then 173 the multiplication result exceeds integer range.
When it comes to undefined behavior - you are at the liberty of the compiler. In optimized builds they tend to compile away some (even large) chunks of code in and around the expression which causes it. And as you just experienced - it may even spread to the code which by itself is correct (the exit condition in the for loop).
BTW integer overflow (as far as I know) causes undefined behavior only for signed integers, it is well defined for unsigned types (the result is truncated). You may want o try i of unsigned type (but this still MAY yield results which you didn't expect).
As explained in the comments, the values generated in the loop, specifically this sub-expression:
(i * 12391823)
overflows for larger values of i. This results in undefined behaviour.
The issue is resolved by using a wider type for i, like long long.
Note: If you use an unsigned type, they will wrap around rather than overflow, if their max limit is exceeded.

if statement inside of for loop not being executed

Writing a program to solve problem four of project euler: Find the largest palindrome made from the product of two 2-digit numbers. Heres my reprex:
#include <iostream>
int reverseNumber(int testNum)
{
int reversedNum, remainder = 0;
int temp = testNum;
while(temp != 0)
{
remainder = temp % 10;
reversedNum = reversedNum * 10 + remainder;
temp /= 10;
}
return reversedNum;
}
int main()
{
const int MIN = 100;
int numOne = 99;
int product = 0;
for(int numTwo = 10; numTwo < 100; numTwo++)
{
product = numOne * numTwo;
if (reverseNumber(product) == product)
{
int solution = product;
std::cout << solution << '\n';
return 0;
}
}
return 0;
}
My main thought process behind this is that the for loop will go through every number from 10 to 99 and multiply it by 99. My intended outcome is for it to print 9009 which is the largest palindrome with 2 factors of 2 digits. So what I think should happen here is the for loop will go from 10 to 99, and each loop it should go through the parameters of the if statement which reverses the number and sees if it equals itself.
I've made sure it wasn't a compiler issue, as this is recurring between different compilers. The reverseNumber() function returns the proper number every time I've tested it, so that shouldn't be the problem, however this problem only occurs when the function is involved in the logical comparison. By this I mean if that even I set it equal to a variable and put the variable in the if parameters, the issue still occurs. I'm pretty much stumped. I just hope it's not some silly mistake as I've been on this for a couple days now.
int reversedNum, remainder = 0;
You should be aware that this gives you (in an automatic variable context) a zero remainder but an arbitrary reversedNum. This is actually one of the reasons some development shops have the "one variable per declaration" rule.
In other words, it should probably be:
int reversedNum = 0, remainder;
or even:
int reversedNum = 0;
int remainder;
One other thing that often helps out is to limit the scope of variable to as small an area as possible, only bringing them into existence when needed. An example of that would be:
int reverseNumber(int testNum) {
int reversedNum = 0;
while (testNum != 0) {
int remainder = testNum % 10;
reversedNum = reversedNum * 10 + remainder;
testNum /= 10;
}
return reversedNum;
}
In fact, I'd probably go further and eliminate remainder altogether since you only use it once:
reversedNum = reversedNum * 10 + testNum % 10;
You'll notice I've gotten rid of temp there as well. There's little to gain by putting testNum into a temporary variable since it's already a copy of the original (as it was passed in by value).
And one other note, more to do with the problem rather than the code. You seem to be assuming that there is a palindrome formed that is a multiple of 99. That may be the case but a cautious programmer wouldn't rely on it - if you're allowed to assume things like that, you could just replace your entire program with:
print 9009
Hence you should probably check all possibilities.
You also get the first one you find which is not necessarily the highest one (for example, let's assume that 99 * 17 and 99 * 29 are both palindromic - you don't want the first one.
And, since you're checking all possibilities, you probably don't want to stop at the first one, even if the nested loops are decrementing instead of incrementing. That's because, if 99 * 3 and 97 * 97 are both palindromic, you want the highest, not the first.
So a better approach may be to start high and do an exhaustive search, while also ensuring you ignore the palindrome check of candidates that are smaller that your current maximum, something like (pseudo-code)
# Current highest palindrome.
high = -1
# Check in reverse order, to quickly get a relatively high one.
for num1 in 99 .. 0 inclusive:
# Only need to check num2 values <= num1: if there was a
# better palindrome at (num2 * num1), we would have
# already found in with (num1 * num2).
for num2 in num1 .. 0 inclusive:
mult = num1 * num2
# Don't waste time doing palindrome check if it's
# not greater than current maximum - we can't use
# it then anyway. Also, if we find one, it's the
# highest possible for THIS num1 value (since num2
# is decreasing), so we can exit the num2 loop
# right away.
if mult > high:
if mult == reversed(mult):
high = mult
break
if high >= 0:
print "Solution is ", high
else:
print "No solution"
In addition to properly initializing your variables, if you want the largest palindrome, you should switch the direction of your for loop -- like:
for(int numTwo = 100; numTwo > 10; numTwo--) {
...
}
or else you are just printing the first palindrome within your specified range

Inifinite loop makes variable come out as 0

I have this piece of code in my school book.
#include<iostream>
using namespace std;
int main() {
int x=10,c=1;
while (c < 5) {
x += x*c;
c *= 2;
c++;
c -= 2;
cout << "X=" << x<<'\n';
}
system("pause");
return 0;
}
As you can see it's an infinite loop, when logically traced, it should show 20,40,80 and so on.
However it always shows 0.
when adding system("pause") after each loop cycle it shows the correct values, but when left as shown above (infinitely looping) it shows zero.
Any ideas of the reason?
c is always 1 no matter what. The loop becomes infinite. Eventually, X becomes 0 due to integer overflow.
c = 1
c *= 2; c = 2
c++; c = 3
c -= 2; c = 1 <-- infinite
Here is my answer for your questions:
Why do you get infinitely looping?
awesomeyi did answer you above, because the condition of the while loop is always true, so it is never ended.
Why does X always equal to 0?
Please pay your attention on X varable, its value will be increased after ending one loop x += x*c. Because you are in the infinitely loop, x's value will be increased forever until greater than the limited value of an integer variable. Then, the value will be set as zero. Please see my output when running your code.
Removing the pause doesn't cause it to always show zero. It just prints output so quickly that zeroes are all you see at the bottom. Add the pause back in and click through about 30-40 iterations and see if it helps you understand what is happening.

Why does this take so long to compile in VCC 2003?

My team need the "Sobol quasi-random number generator" - a common RNG which is famous for good quality results and speed of operation. I found what looks like a simple C implementation on the web. At home I was able to compile it almost instantaneously using my Linux GCC compiler.
The following day I tried it at work: If I compile in Visual Studio in debug mode it takes about 1 minute. If I were to compile it in release mode it takes about 40 minutes.
Why?
I know that "release" mode triggers some compiler optimization... but how on earth could a file this small take so long to optimize? It's mostly comments and static-data. There's hardly anything worth optimizing.
None of these PCs are particularly slow, and in any case I know that the compile time is consistent across a range of Windows computers. I've also heard that newer versions of Visual Studio have a faster compile time, however for now we are stuck with Visual Studio.Net 2003. Compiling on GCC (the one bundled with Ubuntu 8.04) always takes microseconds.
To be honest, I'm not really sure the codes that good. It's got a nasty smell in it. Namely, this function:
unsigned int i4_xor ( unsigned int i, unsigned int j )
//****************************************************************************80
//
// Purpose:
//
// I4_XOR calculates the exclusive OR of two integers.
//
// Modified:
//
// 16 February 2005
//
// Author:
//
// John Burkardt
//
// Parameters:
//
// Input, unsigned int I, J, two values whose exclusive OR is needed.
//
// Output, unsigned int I4_XOR, the exclusive OR of I and J.
//
{
unsigned int i2;
unsigned int j2;
unsigned int k;
unsigned int l;
k = 0;
l = 1;
while ( i != 0 || j != 0 )
{
i2 = i / 2;
j2 = j / 2;
if (
( ( i == 2 * i2 ) && ( j != 2 * j2 ) ) ||
( ( i != 2 * i2 ) && ( j == 2 * j2 ) ) )
{
k = k + l;
}
i = i2;
j = j2;
l = 2 * l;
}
return k;
}
There's an i8_xor too. And a couple of abs functions.
I think a post to the DailyWTF is in order.
EDIT: For the non-c programmers, here's a quick guide to what the above does:
function xor i:unsigned, j:unsigned
answer = 0
bit_position = 1
while i <> 0 or j <> 0
if least significant bit of i <> least significant bit of j
answer = answer + bit_position
end if
bit_position = bit_position * 2
i = i / 2
j = j / 2
end while
return answer
end function
To determine if the least significant bit is set or cleared, the following is used:
bit set if i <> (i / 2) * 2
bit clear if i == (i / 2) * 2
What makes the code extra WTFy is that C defines an XOR operator, namely '^'. So, instead of:
result = i4_xor (a, b);
you can have:
result = a ^ b; // no function call at all!
The original programmer really should have know about the xor operator. But even if they didn't (and granted, it's another obfuscated C symbol), their implementation of an XOR function is unbelievably poor.
I'm using VC++ 2003 and it compiled instantly in both debug/release modes.
Edit:
Do you have the latest service pack installed on your systems?
I would recommend you download a trial edition of Visual Studio 2008 and try the compile there, just to see if the problem is inherent. Also, if it does happen on a current version, you would be able to report the problem, and Microsoft might fix it.
On the other hand, there is no chance that Microsoft will fix whatever bug is in VS2003.