I'm wondering about the use of code like the following
int result = 0; int factor = 1; for (...) { result = ... factor *= 10; } return result;
If the loop is iterated over n
times, then factor
is multiplied by 10
exactly n
times. However, factor
is only ever used after having been multiplied by 10
a total of n-1
times. If we assume that factor
never overflows except on the last iteration of the loop, but may overflow on the last iteration of the loop, then should such code be acceptable? In this case, the value of factor
would provably never be used after the overflow has happened.
I'm having a debate on whether code like this should be accepted. It would be possible to put the multiplication inside an if-statement and just not do the multiplication on the last iteration of the loop when it can overflow. The downside is that it clutters the code and adds an unnecessary branch that would need to check for on all the previous loop iterations. I could also iterate over the loop one fewer time and replicate the loop body once after the loop, again, this complicates the code.
The actual code in question is used in a tight inner-loop that consumes a large chunk of the total CPU time in a real-time graphics application.
12.2. 1 Basics of Integer Overflow In contrast, the C standard says that signed integer overflow leads to undefined behavior where a program can do anything, including dumping core or overrunning a buffer. The misbehavior can even precede the overflow.
So, in C/C++ programming, undefined behavior means when the program fails to compile, or it may execute incorrectly, either crashes or generates incorrect results, or when it may fortuitously do exactly what the programmer intended.
The program may fail to compile, or it may execute incorrectly (either crashing or silently generating incorrect results), or it may fortuitously do exactly what the programmer intended.” In very simple words, an object when modified more than once between two sequence points will result in undefined behaviour.
Unspecified behavior is different from undefined behavior. The latter is typically a result of an erroneous program construct or data, and no requirements are placed on the translation or execution of such constructs.
Compilers do assume that a valid C++ program does not contain UB. Consider for example:
if (x == nullptr) { *x = 3; } else { *x = 5; }
If x == nullptr
then dereferencing it and assigning a value is UB. Hence the only way this could end in a valid program is when x == nullptr
will never yield true and the compiler can assume under the as if rule, the above is equivalent to:
*x = 5;
Now in your code
int result = 0; int factor = 1; for (...) { // Loop until factor overflows but not more result = ... factor *= 10; } return result;
The last multiplication of factor
cannot happen in a valid program (signed overflow is undefined). Hence also the assignment to result
cannot happen. As there is no way to branch before the last iteration also the previous iteration cannot happen. Eventually, the part of code that is correct (i.e., no undefined behaviour ever happens) is:
// nothing :(
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