I have an int x
. For simplicity, say int
s occupy the range -2^31 to 2^31-1. I want to compute 2*x-1
. I allow x
to be any value 0 <= x
<= 2^30. If I compute 2*(2^30), I get 2^31, which is an integer overflow.
One solution is to compute 2*(x-1) 1
. There's one more subtraction than I want, but this shouldn't overflow. However, the compiler will optimize this to 2*x-1
. Is this a problem for the source code? Is this a problem for the executable?
Here is the godbolt output for 2*x-1
.
Here is the godbolt output for 2*(x-1) 1
.
CodePudding user response:
Just because signed integer overflow wasn't well-defined (until C 20) at the C language level doesn't mean that's the case at the assembly level. It's up to the compiler to emit assembly code that is well-defined on the CPU architecture you're targeting.
I'm pretty sure every CPU made in this century has used two's compliment signed integers, and overflow is perfectly well defined for those. That means there is no problem simply calculating 2*x
, letting the result overflow, then subtracting 1 and letting the result underflow back around.
Many such C language-level rules exist to paper over different CPU architectures. In this case, signed integer overflow was made undefined so that compilers targeting CPUs that use i.e. one's compliment or sign/magnitude representations of signed integers aren't forced to add extra instructions to conform to the overflow behavior of two's compliment.
Don't assume, however, that you can use a construct that is well-defined on your target CPU but undefined in C and get the answer you expect. C compilers assume undefined behavior cannot happen when performing optimization, and so they can and will emit different code from what you were expecting if your code isn't well-defined C .
CodePudding user response:
As Miles hinted: The C code text is bound by the rules of the C language (integer overflow = bad), but the compiler is only bound by the rules of the cpu (overflow=ok). It is allowed to make optimizations that the code isn't allowed to.
But don't take this as an excuse to get lazy. If you write undefined behavior, the compiler will take that as a hint and do other optimizations that result in your program doing the wrong thing.