-3
  int tx = INT_MAX +1; // 2147483648;                                                                                                                                                 
  printf("tx = %d\n", tx);

prints tx = -2147483648.

I was wondering how to explain the result based on 6.3 Conversions in C11 standard?

  1. when evaluating INT_MAX +1, are both operands int? Is the result 2147483648 long int? Which rule in 6.3 determines the type of the result?

  2. when evaluating tx = ..., are the higher bits of the bit representation of the right hand side truncated so that its size changes from long int size to int size, and then are the truncated result interpreted as int? What rules in 6.3 determines how the conversion in this step is done?

SecretAgentMan
  • 2,856
  • 7
  • 21
  • 41
Tim
  • 1
  • 141
  • 372
  • 590
  • [C11 6.5p5](http://port70.net/~nsz/c/c11/n1570.html#6.5p5): "*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*" – pmg Oct 15 '20 at 15:24

2 Answers2

3

Both INT_MAX and 1 have type int, so the result will have type int. Performing this operation causes signed integer overflow which is undefined behavior.

Section 3.4.3p3 Gives this as an example of undefined behavior:

EXAMPLE An example of undefined behavior is the behavior on integer overflow.

dbush
  • 205,898
  • 23
  • 218
  • 273
  • If the statement is only `INT_MAX +1;`, not `int tx = INT_MAX +1;`, will the result have type `int` or a larger type? – Tim Oct 15 '20 at 15:17
  • @Tim the resulting type is never wider than the 2 operands – phuclv Oct 15 '20 at 15:19
  • @Tim The type of the result of `+` depends only on the operands, not later operations. – dbush Oct 15 '20 at 15:19
  • 1
    @phuclv: Adding a `char` to a `char` produces an `int`. – Eric Postpischil Oct 15 '20 at 15:32
  • Although you insist on undefinedness, INT_MAX + 1 is INT_MIN by addition with modulo the range of `int`. That is the output of GCC. I think it is not coincidence. – Tim Oct 15 '20 at 16:38
  • 1
    @Tim That's one possible outcome, but the standard makes no guarantees that will happen. Using a different compiler or different settings could generate different results. – dbush Oct 15 '20 at 16:41
  • @Tim See [this question](https://stackoverflow.com/questions/64364307/why-do-both-of-these-c-statements-produce-same-outputs) for an example of when signed integer overflow creates strange results. – dbush Oct 15 '20 at 16:44
  • 1
    @Tim: The question’s title asks “What rules in C11 standard…” The behavior of GCC is not a rule in the C11 standard. Yes, the behavior of GCC is not a coincidence; GCC is designed with many properties beyond in the C standard, including, based on vague recollection, a selectable option to wrap for signed integer arithmetic. But that is not a rule in the C11 standard. – Eric Postpischil Oct 15 '20 at 20:18
  • @EricPostpischil yes, I'm talking about types where default promotion doesn't occur (i.e. types not narrower than `int`) – phuclv Oct 16 '20 at 01:36
  • @Tim GCC also has [`-fwrapv`](https://stackoverflow.com/q/47232954/995714) and [`-ftrapv`](https://gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html) for modifying signed overflow behavior. Since the standard says that it's undefined behavior, anything can happen – phuclv Oct 16 '20 at 01:39
1

The relevant part here is 6.5/5:

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.

This happens because both INT_MAX and the integer constant 1 have types int. So you simply can't do INT_MAX + 1. And there are no implicit promotions/conversions present to save the day, so 6.3 does not apply. It's a bug, anything can happen.


What you could do is to force a conversion by changing the code to int tx = INT_MAX + 1u;. Here one operand, 1u, is of unsigned int type. Therefore the usual arithmetic conversions convert INT_MAX to type unsigned int (See Implicit type promotion rules). The result is a well-defined 2147483648 and of type unsigned int.

Then there's an attempt to store this inside int tx, conversion to the left operand of assignment applies and then the conversion rules of 6.3 kick in. Specifically 6.3.1.3/3:

Otherwise, the new type is signed and the value cannot be represented in it; either the result is implementation-defined or an implementation-defined signal is raised.

So by changing the type to 1u we changed the code from undefined to impl.defined behavior. Still not ideal, but at least now the code has deterministic behavior on the given compiler. In theory, the result could be a SIGFPE signal, but in practice all real-world 2's complement 32/64 bit compilers are likely to give you the result -2147483648.


Ironically, all real-world 2's complement CPUs I've ever heard of perform signed overflow in a deterministic way. So the undefined behavior part of C is just an artificial construct by the C standard, caused by the useless language feature that allows exotic 1's complement and signed magnitude formats. In such exotic formats, signed overflow could lead to a trap representation and so C must claim that integer overflow is undefined behavior, even though it is not on the real-world 2's complement CPU that the C program is executing on.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • 1
    The compiler can still take advantage of UB when overflow happens. [Here's an example](https://stackoverflow.com/questions/64364307/why-do-both-of-these-c-statements-produce-same-outputs). – dbush Oct 16 '20 at 15:15
  • @dbush this is a better example of taking advantage of UB to optimize code: [`-fstrict-overflow`](https://gcc.gnu.org/onlinedocs/gcc-4.9.1/gcc/Optimize-Options.html): *... 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 does not happen. This permits various optimizations. For example, the compiler assumes that an expression like i + 10 > i is always true for signed i.* – phuclv Oct 17 '20 at 01:39
  • More examples: [How undefined signed overflow enables optimizations in GCC](https://kristerw.blogspot.com/2016/02/how-undefined-signed-overflow-enables.html), https://en.wikipedia.org/wiki/Undefined_behavior#Benefits, https://web.archive.org/web/20200921130633/https://blog.llvm.org/posts/2011-05-13-what-every-c-programmer-should-know/, [GCC optimizations based on integer overflow](https://stackoverflow.com/q/23889022/995714) – phuclv Oct 17 '20 at 01:39