4

I was working with integers in C, trying to explore more on when and how overflow happens.

I noticed that when I added two positive numbers, the sum of which overflows, I always got a negative number.

On the other hand, if I added two negative numbers, the sum of which overflows, I always got a positive number (including 0).

I made few experiments, but I would like to know if this is true for every case.

FranXh
  • 4,481
  • 20
  • 59
  • 78
  • Underflow and overflow are undefined for signed integers, but well-defined for unsigned integers. Assuming those positive values you added were not unsigned, anything *could* happen. – chris Sep 09 '12 at 01:18
  • Though "undefined", virtually every modern computer used "2's complement" arithmetic, and so for practical purposes it is defined and works the way you're thinking, except perhaps for a handful of "boundary" cases that I can't get my head around right now. – Hot Licks Sep 09 '12 at 01:23
  • 1
    @HotLicks, You reminded me of [this C++ question](http://stackoverflow.com/questions/7682477/gcc-fail-or-undefined-behavior) where an exception was exactly what happened. – chris Sep 09 '12 at 01:26
  • @chris -- Of course, in that case the arithmetic did (apparently) wrap, it's just that the compiler assumed that the arithmetic would not pass into the "undefined" region and hence optimized away a test. The "undefined" rule is used to permit optimizations that wouldn't be possible if the wrapping behavior was in the spec. – Hot Licks Sep 09 '12 at 02:26
  • @HotLicks, And that's why UB in C++ is a *good* thing :D – chris Sep 09 '12 at 02:31

4 Answers4

13

Integer overflows are undefined behavior in C.

C says an expression involving integers overflows, if its result after the usual arithmetic conversions is of a signed typed and cannot be represented in the type of the result. Assignment and cast expressions are an exception as they are ruled by the integer conversions.

Expressions of unsigned type cannot overflow, they wrap, e. g., 0U - 1 is UINT_MAX.

Examples:

INT_MAX + 1    // integer overflow
UINT_MAX + 1   // no overflow, the resulting type is unsigned
(unsigned char) INT_MAX // no overflow, integer conversion occurs 

Never let any integer expression overflows, modern compilers (like gcc) take advantage of integer overflows being undefined behavior to perform various types of optimizations.

For example:

a - 10 < 20

when a is of type int after promotion, the expression is reduced in gcc (when optimization are enabled) to:

a < 30

It takes advantage of the expression being undefined behavior when a is in the range INT_MIN + 10 - 1 to INT_MIN.

This optimization could not be done when a is unsigned int because if a is 0, then a - 10 has to be evaluated as UINT_MAX - 9 (no undefined behavior). Optimizing a - 10 < 20 to a < 30 would then lead to a different result than the required one when a is 0 to 9.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
ouah
  • 142,963
  • 15
  • 272
  • 331
2

Overflow of signed integers is undefined behaviour in C, so there are no guarantees.

That said, wrap around, or arithmetic modulo 2N, where N is the number of bits in the type, is a common behaviour. For that behaviour, indeed if a sum overflows, the result has the opposite sign of the operands.

Daniel Fischer
  • 181,706
  • 17
  • 308
  • 431
1

Formally, the behaviour of signed arithmetic on overflow is undefined; anything can happen and it is 'correct'. This contrasts with unsigned arithmetic, where overflow is completely defined.

In practice, many older compilers used signed arithmetic which overflowed as you describe. However, modern GCC is making changes to the way it works, and you'd be very ill-advised to rely on the behaviour. It may change at any time when anything in the environment where your code is compiled changes — the compiler, the platform, ...

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • Huh, do you have a reference to GCC changes? I'd be interested to read that. – chris Sep 09 '12 at 01:23
  • Not specifically, but if you see discussions in the `comp.std.c`, `comp.lang.c` or `comp.lang.c.moderated` news groups, you will find references to some changes — at least where the compiler can determine (necessarily at compile time) that overflow will occur. – Jonathan Leffler Sep 09 '12 at 01:28
  • Sounds useful to me. That should save C++ newbies some trouble if they happen to get a newer version of GCC. I don't think 4.8 or anything is coming for a while yet, either, or which version it'll be included in. – chris Sep 09 '12 at 01:30
  • 1
    Related: in his blog post [Undefined behavior can result in time travel](https://devblogs.microsoft.com/oldnewthing/20140627-00/?p=633), Microsoft developer Raymond Chen compares this type of optimizations between what he calls _classical compilers_ and _post-classical_ or _post-modern compilers_. So, yes, the behavior of compilers has indeed changed in this respect. – wovano Mar 20 '22 at 12:07
1

Overflow in C is a godawful mess.

  • Overflow during unsigned arithmetic or conversion to an unsigned type results in wraping modulo 2n
  • Overflow during conversion to a signed type is implementation defined, most implementations will wrap modulo 2n but some may not.
  • Overflow during signed arithmetic is undefined behaviour, according to the standard anything might happen. In practice sometimes it will do what you wan't, sometimes it will cause strange issues later in yoir code as the compiler optimises out important tests.

What makes things even worse is how this interacts with integer promotion. Thanks to promotion you can be doing signed arithmetic when it looks like you are doing unsigned arithmetic. For example consider the following code

uint16_t a = 65535;
uint16_t b = a * a;

On a system with 16-bit int this code is well-defined. However on a system with 32-bit int the multiplication will take place as signed int and the resulting overflow will be undefined behavior!

plugwash
  • 9,724
  • 2
  • 38
  • 51