Why does exactly -LLONG_MIN lead to undefined behavior?

95 views Asked by At

Why does exactly -LLONG_MIN lead to undefined behavior?

C11, 6.5.3.3p3:

The result of the unary - operator is the negative of its (promoted) operand. The integer promotions are performed on the operand, and the result has the promoted type.

Here we see that the C standard doesn't require that unary - operator to be implemented, for example, using bitwise-not followed by adding 1, where "adding 1" may lead to undefined behavior.

How to deduce from the quoted text that -LLONG_MIN leads to undefined behavior?

3

There are 3 answers

0
KamilCuk On BEST ANSWER

How to deduce from the quoted text that -LLONG_MIN leads to undefined behavior?

It is not possible to deduce it from the quoted text. You also need this text https://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.

On implementations in which the mathematical result of -LLONG_MIN is not representable in long long type, this rule is triggered and behavior is undefined.

3
Eric Postpischil On

How to deduce from the quoted text that -LLONG_MIN leads to undefined behavior?

It cannot be deduced from the quoted text alone. However, this chain of reasoning suffices:

  • The C standard permits LLONG_MIN to be −9,223,372,036,854,775,808 while LLONG_MAX is 9,223,372,036,854,775,807 (C 2018 5.2.4.2.1 1).
  • There exists (in the mathematical sense) a C implementation in which LLONG_MIN and LLONG_MAX have those values. (I assume the reader is familiar with two’s complement and that the standard allows two’s complement.)
  • In such a C implementation, the specified nominal result of -LLONG_MIN would be 9,223,372,036,854,775,808 (C 2018 6.5.3.3 3).
  • The type of LLONG_MIN is long long int (C 2018 5.2.4.2.1 1), and the type of the result of -LLONG_MIN is long long int (C 2018 6.5.3.3 3 and 6.3.1.1).
  • In said C implementation, that value would not be representable in long long int (due to the meanings of LLONG_MIN and LLONG_MAX).
  • C 6.5 5 says:

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.

1
nielsen On

As the other answers have pointed out, this case is covered by paragraph 6.5-5 which says that the behavior is undefined if the result is not representable by its type.

Generally, undefined behavior may be a case where the behavior is simply not defined (mentioned) in the C standard, but in this case the standard is quite explicit.

When it comes to signed integer types, the C18 standard (and earlier) allows 3 representations (paragraph 6.2.6.2-2)

  • Sign and magnitude
  • Two's complement
  • One's complement

With "two's complement" representation, the negative value of the minimal representable value is not representable (it will be one more than the maximal representable value).

With "sign and magnitude" and "one's complement", the negative value of the minimal representable value is representable (it will equal the maximal representable value).

Thus, -LLONG_MIN is undefined behavior if and only if the implementation uses the "two's complement" representation.

Since the Standard thus allows for implementations where this is undefined behavior, the behavior is undefined in terms of the Standard.

It is always allowed for an implementation to define behavior in cases where the behavior is undefined by the Standard, but in this case the Standard explicitly characterizes a group of implementations which have a defined behavior of -LLONG_MIN (those not using two's complement representation of signed integers).

It can be checked via a static assertion if the implementation has -LLONG_MAX==LLONG_MIN, but it is of little practical use since two's complement representation is by very far the most common.

The coming C2x standard will only allow two's complement representation (paragraph 6.2.6.2-2). So with C2x, -LLONG_MIN will always be undefined behavior in terms of the Standard.