g++ strict overflow, optimization, and warnings - c++

When compiling the following with the strict overflow flag, it tells me, on the 2nd test that r may not be what I think it could be:
int32_t r(my_rand());
if(r < 0) {
r = -r;
if(r < 0) { // <-- error on this line
r = 0;
}
}
The error is:
/build/buildd/libqtcassandra-0.5.5/tests/cassandra_value.cpp:
In function 'int main(int, char**)':
/build/buildd/libqtcassandra-0.5.5/tests/cassandra_value.cpp:2341:13:
error: assuming signed overflow does not occur when simplifying
conditional to constant [-Werror=strict-overflow]
if(r < 0) {
^
What I do not understand is: why wouldn't the error be generated on the line before that? Because really the overflow happens when I do this, right?
r = -r;

EDIT: I removed my first answer, because it was invalid. Here is completely new version. Thanks to #Neil Kirk for pointing out my errors.
Answer for the question is here: https://stackoverflow.com/a/18521660/2468549
GCC always assumes, that signed overflow does never occur, and, on that assumption, it (always) optimizes out the inner if (r < 0) block.
If you turn -Wstrict-overflow on, then compiler finds out, that after r = -r r < 0 may still be true (if r == -2^31 initially), which causes an error (error is caused by optimization based on assumption of overflow never occurring, not by overflow possibility itself - that's how -Wstrict-overflow works).

A couple of additions in regard to having such overflows:
You can turn off these warnings with a #pragma GCC ...
If you are sure that your code will always work (wrapping of integers is fine in your function) then you can use a pragma around the offensive block or function:
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstrict-overflow"
...offensive code here...
#pragma GCC diagnostic pop
Of course, this means you are completely ignoring the errors and let the compiler do its optimizations. Better have a test to make 100% sure that edge cases work as expected!
Use unsigned integers
The documentation says:
For C (and C++) this means that overflow when doing
arithmetic with signed numbers is undefined, which
means that the compiler may assume that it will not
happen.
In other words, if the math uses unsigned integers, this optimization doesn't apply. So if you need to do something such as r = -r, you can first copy r in an unsigned integer of the same size and then do the sign change:
std::int32_t r = ...;
std::uint32_t q = r;
q = -q;
if(static_cast<std::int32_t>(q) < 0) ...
That should work as expected: the if() statement will not be optimized out.
Turn off the actual optimization
Note: This was seemingly working in older compilers, but that is not currently possible on a per function basis. You have to use that command line option on the command line and not as an attribute. I leave this here for now as it may work for you.
I like this one better. I bumped in a test that would fail in Release mode today. Nothing that I use on a regular basis and only for a very specific edge case, but it worked just fine in Debug, therefore, not having the compiler optimize that function is a better option. You can actually do so as follow:
T __attribute__((optimize("-fno-strict-overflow"))) func(...)
{
...
}
That attribute cancels the -fstrict-overflow¹ error by actually emitted code as expected. My test now passes in Debug and Release.
Note: this is g++ specific. See your compiler documentation for equivalents, if available.
Either way, as mentioned by Frax, in -O3 mode, the compiler wants to optimize the test by removing it and the whole block of code. The whole block can be removed because if it were negative, after the r = -r;, then r is expected to be positive. So testing for a negative number again is optimized out and that's what the compiler is warning us about. However, with the -fstrict-overflow attribute, you instead ask the compiler to do that optimization in that one function. As a result you get the expected behavior in all cases, including overflows.
¹ I find that option name confusing. In this case, if you use -fstrict-overflow, you ask for the optimizer to do optimization even if strict overflows are not respected as a result.

Related

C++ while loop optimization not working properly

I have this code segment:
#include <stdio.h>
int main(int argc, const char** argv)
{
int a = argv[0][0];
int b = argv[0][1];
while ((a >= 0) &&
(a < b))
{
printf("a = %d\n", a);
a++;
}
return 0;
}
and I'm compiling it with gcc-4.5 -02 -Wstrict-overflow=5.
The compiler yells at me
warning: assuming signed overflow does not occur when changing X +- C1 cmp C2 to X cmp C1 +- C2
What does this mean exactly?
If i am correct, this loop will never cause an overflow, because for a to be incremented, it must be smaller than another integer. If it is bigger, the loop is terminated.
Can anyone explain this behavior to me?
The compiler is making an optimisation to convert a + 1 < b to a < b - 1.
However, if b is INT_MIN then this will underflow, which is a change in behaviour.
That's what it's warning about.
Of course, you can probably tell that this is impossible, but the compiler has limited resources to work things out and generally won't do in-depth analysis on data paths.
Adding a check that b >= 0 may solve the problem.
Edit: Another possibility is that it's moving a >= 0 to outside the loop, as (assuming no overflow) it can never change. Again, the assumption may not be valid for all inputs (i.e. if b is negative). You would need check the final assembly to see what it actually did.
The C++ standard says that if a signed integer calculation produces a result outside the representable range for the type then the behaviour is undefined. Integer overflow is UB. Once UB has happened, the implementation is free to do whatever it likes.
Many compilers apply optimisations on the explicit assumption that UB does not happen. [Or if it does, the code could be wrong but it's your problem!]
This compiler is notifying you that it is applying such an optimisation to a calculation where it is unable to determine from analysing the code that UB does not happen.
Your choices in general are:
Satisfy yourself that UB cannot happen, and ignore the warning.
Allow UB to happen and live with the consequences.
Rewrite the code so UB really cannot happen and the compiler knows it cannot happen, and the warning should go away.
I would recommend the last option. Simple range tests on a and b should be good enough.
My guess is that the compiler emits this error because the loop deals with completely unknown values, and it is unable to analyse the data flow well enough to work out whether UB can happen or not.
We with our superior reasoning power can convince ourselves that UB cannot happen, so we can ignore the error. In fact a careful reading of the error message might leave us asking whether it is relevant at all. Where are these two constant value C1 and C2?
We might also note that a can never go negative, so why is that test in the loop? I would probably rewrite the code to suppress the error, (but from experience that can be a self-defeating exercise). Try this and see what happens (and avoid unneeded parenthetic clutter):
if (a >= 0) {
while (a < b) {
...
++a;
}
}
What the compiler is warning you about is that it is assuming that signed overflow does not take place in the original code.
The warning does not mean "I'm about to write an optimization which potentially introduces overflows."
In other words, if your program depends on overflow (i.e. is not highly portable), then the optimization the compiler is doing may change its behavior. (So please verify for yourself that this code doesn't depend on overflow).
For instance, if you have "a + b > c", and you're depending on this test failing when a + b arithmetic wraps around (typical two's complement behavior), then if this happens to be algebraically transformed to "a > c - b", then it might succeed, because c - b may happen not to overflow, and produce a value smaller than a.
Notes: Only C programs can invoke undefined behavior. When compilers are incorrect, they are "nonconforming". A compiler can only be non-conforming (to the C standard) if it does the wrong thing with (C standard) conforming code. An optimization which alters correct, portable behavior is a nonconforming optimization.

Why does this code compile without warnings?

I have no idea why this code complies :
int array[100];
array[-50] = 100; // Crash!!
...the compiler still compiles properly, without compiling errors, and warnings.
So why does it compile at all?
array[-50] = 100;
Actually means here:
*(array - 50) = 100;
Take into consideration this code:
int array[100];
int *b = &(a[50]);
b[-20] = 5;
This code is valid and won't crash. Compiler has no way of knowing, whether the code will crash or not and what programmer wanted to do with the array. So it does not complain.
Finally, take into consideration, that you should not rely on compiler warnings while finding bugs in your code. Compilers will not find most of your bugs, they barely try to make some hints for you to ease the bugfixing process (sometimes they even may be mistaken and point out, that valid code is buggy). Also, the standard actually never requires the compiler to emit warning, so these are only an act of good will of compiler implementers.
It compiles because the expression array[-50] is transformed to the equivalent
*(&array[0] + (-50))
which is another way of saying "take the memory address &array[0] and add to it -50 times sizeof(array[0]), then interpret the contents of the resulting memory address and those following it as an int", as per the usual pointer arithmetic rules. This is a perfectly valid expression where -50 might really be any integer (and of course it doesn't need to be a compile-time constant).
Now it's definitely true that since here -50 is a compile-time constant, and since accessing the minus 50th element of an array is almost always an error, the compiler could (and perhaps should) produce a warning for this.
However, we should also consider that detecting this specific condition (statically indexing into an array with an apparently invalid index) is something that you don't expect to see in real code. Therefore the compiler team's resources will be probably put to better use doing something else.
Contrast this with other constructs like if (answer = 42) which you do expect to see in real code (if only because it's so easy to make that typo) and which are hard to debug (the eye can easily read = as ==, whereas that -50 immediately sticks out). In these cases a compiler warning is much more productive.
The compiler is not required to catch all potential problems at compile time. The C standard allows for undefined behavior at run time (which is what happens when this program is executed). You may treat it as a legal excuse not to catch this kind of bugs.
There are compilers and static program analyzers that can do catch trivial bugs like this, though.
True compilers do (note: need to switch the compiler to clang 3.2, gcc is not user-friendly)
Compilation finished with warnings:
source.cpp:3:4: warning: array index -50 is before the beginning of the array [-Warray-bounds]
array[-50] = 100;
^ ~~~
source.cpp:2:4: note: array 'array' declared here
int array[100];
^
1 warning generated.
If you have a lesser (*) compiler, you may have to setup the warning manually though.
(*) ie, less user-friendly
The number inside the brackets is just an index. It tells you how many steps in memory to take to find the number you're requesting. array[2] means start at the beginning of array, and jump forwards two times.
You just told it to jump backwards 50 times, which is a valid statement. However, I can't imagine there being a good reason for doing this...

Don't understand "assuming signed overflow" warning

I am getting:
warning: assuming signed overflow does not occur when assuming that (X + c) < X is always false [-Wstrict-overflow]
on this line:
if ( this->m_PositionIndex[in] < this->m_EndIndex[in] )
m_PositionIndex and m_EndIndex of type itk::Index (http://www.itk.org/Doxygen/html/classitk_1_1Index.html), and their operator[] returns a signed long.
(it is line 37 here: https://github.com/Kitware/ITK/blob/master/Modules/Core/Common/include/itkImageRegionConstIteratorWithIndex.hxx for context)
Can anyone explain what would cause that warning here? I don't see the pattern (x+c) < x anywhere - as this is simply a signed long comparison.
I tried to reproduce it in a self-contained example:
#include <iostream>
namespace itk
{
struct Index
{
signed long data[2];
Index()
{
data[0] = 0;
data[1] = 0;
}
signed long& operator[](unsigned int i)
{
return data[i];
}
};
}
int main (int argc, char *argv[])
{
itk::Index positionIndex;
itk::Index endIndex;
for(unsigned int i = 0; i < 2; i++)
{
positionIndex[i]++;
if ( positionIndex[i] < endIndex[i] )
{
std::cout << "something" << std::endl;
}
}
return 0;
}
but I do not get the warning there. Any thoughts as to what is different between my demo and the real code, or what could be causing the warning in the real code? I get the warning with both gcc 4.7.0 and 4.7.2 with the -Wall flag.
To simply disable this warning, use -Wno-strict-overflow. To instead disable the specific optimization that triggers this warning, use -fno-strict-overflow or -fwrapv.
The gcc manpage describes that this warning can be controlled with levels: -Wstrict-overflow=n.
If this is stopping your build due to -Werror, you can work-around without hiding the warnings by using -Wno-error=strict-overflow (or just -Wno-error to override -Werror).
Analysis and commentary...
I got the same warning and spent a couple of hours trying to reproduce it in a smaller example, but never succeeded. My real code involved calling an inline function in a templated class, but the algorithm simplifies to the following...
int X = some_unpredictable_value_well_within_the_range_of_int();
for ( int c=0; c<4; c++ ) assert( X+c >= X ); ## true unless (X+c) overflows
In my case the warning was somehow correlated with the optimizer unrolling the for loop, so I was able to work-around by declaring volatile int c=0. Another thing that fixed it was to declare unsigned int c=0, but I'm not exactly sure why that makes a difference. Another thing that fixed it was making the loop count large enough that the loop wouldn't be unrolled, but that's not a useful solution.
So what is this warning really saying? Either it is saying that the optimizer has modified the semantics of your algorithm (by assuming no overflow), or it is simply informing you that the optimizer is assuming that your code doesn't have the undefined behavior of overflowing a signed integer. Unless overflowing signed integers is part of the intended behavior of your program, this message probably does not indicate a problem in your code -- so you will likely want to disable it for normal builds. If you get this warning but aren't sure about the code in question, it may be safest to just disable the optimization with -fwrapv.
By the way, I ran into this issue on GCC 4.7, but the same code compiled without warning using 4.8 -- perhaps indicating that the GCC developers recognized the need to be a bit less "strict" in the normal case (or maybe it was just due to differences in the optimizer).
Philosophy...
In [C++11:N3242$5.0.4], it states...
If during the evaluation of an expression, the result is not
mathematically defined or not in the range of representable values for
its type, the behavior is undefined.
This means that a conforming compiler can simply assume that overflow never occurs (even for unsigned types).
In C++, due to features such as templates and copy constructors, elision of pointless operations is an important optimizer capability. Sometimes though, if you are using C++ as a low-level "system language", you probably just want the compiler to do what you tell it to do -- and rely on the behavior of the underlying hardware. Given the language of the standard, I'm not sure how to achieve this in a compiler-independent fashion.
The compiler is telling you that it has enough static knowledge of that snippet to know that test will always succeed if it can optimize the test assuming that no signed operation will overflow.
In other words, the only way x + 1 < x will ever return true when x is signed is if x is already the maximum signed value. [-Wstrict-overflow] let's the compiler warn when it can assume that no signed addition will overflow; it's basically telling you it's going to optimize away the test because signed overflow is undefined behavior.
If you want to suppress the warning, get rid of the test.
Despite the age of your question, since you didn't change that part of your code yet, I assume the problem still exists and that you still didn't get a useful explanation on what's actually going on here, so let my try it:
In C (and C++), if adding two SIGNED integers causes an overflow, the behavior of the entire program run is UNDEFINED. So, the environment that executes your program can do whatever it wants (format your hard disk or start a nuklear war, assuming the necessary hardware is present).
gcc usually does neither, but it does other nasty things (that could still lead to either one in unlucky circumstances). To demonstrate this, let me give you a simple example from Felix von Leitner # http://ptrace.fefe.de/int.c:
#include <assert.h>
#include <stdio.h>
int foo(int a) {
assert(a+100 > a);
printf("%d %d\n",a+100,a);
return a;
}
int main() {
foo(100);
foo(0x7fffffff);
}
Note: I added stdio.h, to get rid of the warning about printf not being declared.
Now, if we run this, we would expect the code to assert out on the second call of foo, because it creates an integer overflow and checks for it. So, let's do it:
$ gcc -O3 int.c -o int && ./int
200 100
-2147483549 2147483647
WTF? (WTF, German for "Was täte Fefe" - "what would Fefe do", Fefe being the nick name of Felix von Leitner, which I borrowed the code example from). Oh, and, btw, Was täte Fefe? Right: Write a bug report in 2007 about this issue! https://gcc.gnu.org/bugzilla/show_bug.cgi?id=30475
Back to your question. If you now try to dig down, you could create an assembly output (-S) and investigate only to figure out, that the assert was completely removed
$ gcc -S -O3 int.c -o int.s && cat int.s
[...]
foo:
pushq %rbx // save "callee-save" register %rbx
leal 100(%rdi), %edx // calc a+100 -> %rdx for printf
leaq .LC0(%rip), %rsi // "%d %d\n" for printf
movl %edi, %ebx // save `a` to %rbx
movl %edi, %ecx // move `a` to %rcx for printf
xorl %eax, %eax // more prep for printf
movl $1, %edi // and even more prep
call __printf_chk#PLT // printf call
movl %ebx, %eax // restore `a` to %rax as return value
popq %rbx // recover "callee-save" %rbx
ret // and return
No assert here at any place.
Now, let's turn on warnings during compilation.
$ gcc -Wall -O3 int.c -o int.s
int.c: In function 'foo':
int.c:5:2: warning: assuming signed overflow does not occur when assuming that (X + c) >= X is always true [-Wstrict-overflow]
assert(a+100 > a);
^~~~~~
So, what this message actually says is: a + 100 could potentially overflow causing undefined behavior. Because you are a highly skilled professional software developer, who never does anything wrong, I (gcc) know for sure, that a + 100 will not overflow. Because I know that, I also know, that a + 100 > a is always true. Because I know that, I know that the assert never fires. And because I know that, I can eliminate the entire assert in 'dead-code-elimination' optimization.
And that is exactly, what gcc does here (and warns you about).
Now, in your small example, the data flow analysis can determine, that this integer in fact does not overflow. So, gcc does not need to assume it to never overflow, instead, gcc can prove it to never overflow. In this case, it's absolutely ok to remove the code (the compiler could still warn about dead code elimination here, but dce happen so often, that probably nobody wants those warnings). But in your "real world code", the data flow analysis fails, because not all necessary information is present for it to take place. So, gcc doesn't know, whether a++ overflows. So it warns you, that it assumes that never happens (and then gcc removes the entire if statement).
One way to solve (or hide !!!) the issue here would be to assert for a < INT_MAX prior to doing the a++. Anyway, I fear, you might have some real bug there, but I would have to investigate much more to figure it out. However, you can figure it out yourself, be creating your MVCE the proper way: Take that source code with the warning, add anything necessary from include files to get the source code stand-alone (gcc -E is a little bit extreme but would do the job), then start removing anything that doesn't make the warning disappear until you have a code where you can't remove anything anymore.
In my case, this was a good warning from the compiler. I had a stupid copy/paste bug where I had a loop inside a loop, and each was the same set of conditions. Something like this:
for (index = 0; index < someLoopLimit; index++)
{
// blah, blah, blah.....
for (index = 0; index < someLoopLimit; index++)
{
// More blah, blah, blah....
}
}
This warning saved me time debugging. Thanks, gcc!!!
This is almost certainly an error, even though I cannot tell which of three possible errors it is.
I believe, that gcc is somehow figuring out what the values in 'this->m_PositionIndex[in]' etc. are computed from, namely that both values are derived from the same value, one with an offset it can also prove to be positive. Consequently it figures, that one branch of the if/else construct is unreachable code.
This is bad. Definitely. Why? Well, there are exactly three possibilities:
gcc is right in that the code is unreachable, and you overlooked that this is the case. In this case you simply have bloated code. Best of the three possibilities, but still bad in terms of code quality.
gcc is right in that the code is unreachable, but you wanted it to be reachable. In this case, you need to take a second look at why it is not reachable and fix the bug in your logic.
gcc is wrong, you have stumbled on a bug in it. Quite unlikely, but possible, and unfortunately very hard to prove.
The really bad thing is, that you absolutely need to either find out exactly why the code is unreachable, or you need to disprove that gcc is right (which is easier said than done). According to Murphy's law, assuming case 1. or 3. without proving it will make sure that it was case 2., and that you will have to hunt for the bug a second time...
I think that the compiler changed
positionIndex[i]++;
if ( positionIndex[i] < endIndex[i] )
into something more optimized like
if ( positionIndex[i]++ < endIndex[i] ) <-- see my comment below, this statement is wrong.
so
if ( positionIndex[i] + 1 < endIndex[i] )
that causes undefined behavior in case of overflow (thanks Seth).

Visual Studio C++ compiler optimizations breaking code?

I've a peculiar issue here, which is happening both with VS2005 and 2010. I have a for loop in which an inline function is called, in essence something like this (C++, for illustrative purposes only):
inline double f(int a)
{
if (a > 100)
{
// This is an error condition that shouldn't happen..
}
// Do something with a and return a double
}
And then the loop in another function:
for (int i = 0; i < 11; ++i)
{
double b = f(i * 10);
}
Now what happens is that in debug build everything works fine. In release build with all the optimizations turned on this is, according to disassembly, compiled so that i is used directly without the * 10 and the comparison a > 100 turns into a > 9, while I guess it should be a > 10. Do you have any leads as to what might make the compiler think that a > 9 is the correct way? Interestingly, even a minor change (a debug printout for example) in the surrounding code makes the compiler use i * 10 and compare that with the literal value of 100.
I know this is somewhat vague, but I'd be grateful for any old idea.
EDIT:
Here's a hopefully reproducable case. I don't consider it too big to be pasted here, so here goes:
__forceinline int get(int i)
{
if (i > 600)
__asm int 3;
return i * 2;
}
int main()
{
for (int i = 0; i < 38; ++i)
{
int j = (i < 4) ? 0 : get(i * 16);
}
return 0;
}
I tested this with VS2010 on my machine, and it seems to behave as badly as the original code I'm having problems with. I compiled and ran this with the IDE's default empty C++ project template, in release configuration. As you see, the break should never be hit (37 * 16 = 592). Note that removing the i < 4 makes this work, just like in the original code.
For anyone interested, it turned out to be a bug in the VS compiler. Confirmed by Microsoft and fixed in a service pack following the report.
First, it'd help if you could post enough code to allow us to reproduce the issue. Otherwise you're just asking for psychic debugging.
Second, it does occasionally happen that a compiler fails to generate valid code at the highest optimization levels, but more likely, you just have a bug somewhere in your code. If there is undefined behavior somewhere in your code, that means the assumptions made by the optimizer may not hold, and then the compiler can end up generating bad code.
But without seeing your actual code, I can't really get any more specific.
The only famous bug I know with optimization (and only with highest optimization level) are occasional modifications of the orders of priority of the operations (due to change of operations performed by the optimizer, looking for the fastest way to compute). You could look in this direction (and put some parenthesis even though they are not strictly speaking necessary, which is why more parenthesis is never bad), but frankly, those kind of bugs are quite rare.
As stated, it difficult to have any precise idea without more code.
Firstly, inline assembly prevents certain optimizations, you should use the __debugbreak() intrinsic for int3 breakpointing. The compiler sees the inline function having no effect other than a breakpoint, so it divides the 600 by 16(note: this is affected by integer truncation), thus it optimizes to debugbreak to trigger with 38 > i >= 37. So it seems to work on this end

gcc optimization? bug? and its practial implication to project

My questions are divided into three parts
Question 1
Consider the below code,
#include <iostream>
using namespace std;
int main( int argc, char *argv[])
{
const int v = 50;
int i = 0X7FFFFFFF;
cout<<(i + v)<<endl;
if ( i + v < i )
{
cout<<"Number is negative"<<endl;
}
else
{
cout<<"Number is positive"<<endl;
}
return 0;
}
No specific compiler optimisation options are used or the O's flag is used. It is basic compilation command g++ -o test main.cpp is used to form the executable.
The seemingly very simple code, has odd behaviour in SUSE 64 bit OS, gcc version 4.1.2. The expected output is "Number is negative", instead only in SUSE 64 bit OS, the output would be "Number is positive".
After some amount of analysis and doing a 'disass' of the code, I find that the compiler optimises in the below format -
Since i is same on both sides of comparison, it cannot be changed in the same expression, remove 'i' from the equation.
Now, the comparison leads to if ( v < 0 ), where v is a constant positive, So during compilation itself, the else part cout function address is added to the register. No cmp/jmp instructions can be found.
I see that the behaviour is only in gcc 4.1.2 SUSE 10. When tried in AIX 5.1/5.3 and HP IA64, the result is as expected.
Is the above optimisation valid?
Or, is using the overflow mechanism for int not a valid use case?
Question 2
Now when I change the conditional statement from if (i + v < i) to if ( (i + v) < i ) even then, the behaviour is same, this atleast I would personally disagree, since additional braces are provided, I expect the compiler to create a temporary built-in type variable and them compare, thus nullify the optimisation.
Question 3
Suppose I have a huge code base, an I migrate my compiler version, such bug/optimisation can cause havoc in my system behaviour. Ofcourse from business perspective, it is very ineffective to test all lines of code again just because of compiler upgradation.
I think for all practical purpose, these kinds of error are very difficult to catch (during upgradation) and invariably will be leaked to production site.
Can anyone suggest any possible way to ensure to ensure that these kind of bug/optimization does not have any impact on my existing system/code base?
PS :
When the const for v is removed from the code, then optimization is not done by the compiler.
I believe, it is perfectly fine to use overflow mechanism to find if the variable is from MAX - 50 value (in my case).
Update(1)
What would I want to achieve? variable i would be a counter (kind of syncID). If I do offline operation (50 operation) then during startup, I would like to reset my counter, For this I am checking the boundary value (to reset it) rather than adding it blindly.
I am not sure if I am relying on the hardware implementation. I know that 0X7FFFFFFF is the max positive value. All I am doing is, by adding value to this, I am expecting the return value to be negative. I don't think this logic has anything to do with hardware implementation.
Anyways, all thanks for your input.
Update(2)
Most of the inpit states that I am relying on the lower level behavior on overflow checking. I have one questions regarding the same,
If that is the case, For an unsigned int how do I validate and reset the value during underflow or overflow? like if v=10, i=0X7FFFFFFE, I want reset i = 9. Similarly for underflow?
I would not be able to do that unless I check for negativity of the number. So my claim is that int must return a negative number when a value is added to the +MAX_INT.
Please let me know your inputs.
It's a known problem, and I don't think it's considered a bug in the compiler. When I compile with gcc 4.5 with -Wall -O2 it warns
warning: assuming signed overflow does not occur when assuming that (X + c) < X is always false
Although your code does overflow.
You can pass the -fno-strict-overflow flag to turn that particular optimization off.
Your code produces undefined behavior. C and C++ languages has no "overflow mechanism" for signed integer arithmetic. Your calculations overflow signed integers - the behavior is immediately undefined. Considering it form "a bug in the compiler or not" position is no different that attempting to analyze the i = i++ + ++i examples.
GCC compiler has an optimization based on that part of the specification of C/C++ languages. It is called "strict overflow semantics" or something lake that. It is based on the fact that adding a positive value to a signed integer in C++ always produces a larger value or results in undefined behavior. This immediately means that the compiler is perfectly free to assume that the sum is always larger. The general nature of that optimization is very similar to the "strict aliasing" optimizations also present in GCC. They both resulted in some complaints from the more "hackerish" parts of GCC user community, many of whom didn't even suspect that the tricks they were relying on in their C/C++ programs were simply illegal hacks.
Q1: Perhaps, the number is indeed positive in a 64bit implementation? Who knows? Before debugging the code I'd just printf("%d", i+v);
Q2: The parentheses are only there to tell the compiler how to parse an expression. This is usually done in the form of a tree, so the optimizer does not see any parentheses at all. And it is free to transform the expression.
Q3: That's why, as c/c++ programmer, you must not write code that assumes particular properties of the underlying hardware, such as, for example, that an int is a 32 bit quantity in two's complement form.
What does the line:
cout<<(i + v)<<endl;
Output in the SUSE example? You're sure you don't have 64bit ints?
OK, so this was almost six years ago and the question is answered. Still I feel that there are some bits that have not been adressed to my satisfaction, so I add a few comments, hopefully for the good of future readers of this discussion. (Such as myself when I got a search hit for it.)
The OP specified using gcc 4.1.2 without any special flags. I assume the absence of the -O flag is equivalent to -O0. With no optimization requested, why did gcc optimize away code in the reported way? That does seem to me like a compiler bug. I also assume this has been fixed in later versions (for example, one answer mentions gcc 4.5 and the -fno-strict-overflow optimization flag). The current gcc man page states that -fstrict-overflow is included with -O2 or more.
In current versions of gcc, there is an option -fwrapv that enables you to use the sort of code that caused trouble for the OP. Provided of course that you make sure you know the bit sizes of your integer types. From gcc man page:
-fstrict-overflow
.....
See also the -fwrapv option. Using -fwrapv means that integer signed overflow
is fully defined: it wraps. ... With -fwrapv certain types of overflow are
permitted. For example, if the compiler gets an overflow when doing arithmetic
on constants, the overflowed value can still be used with -fwrapv, but not otherwise.