Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

int promotion: Is the following well-defined?

Tags:

c

standards

c99

Suppose that on a C implementation (e.g. on a x86 C compiler) USHRT_MAX = 65535 and INT_MAX = 2147483647. Is, then, the following statement well-defined?

unsigned short product = USHRT_MAX * USHRT_MAX;

According to the following in the C99 standard both operands are promoted to int (since int can represent all possible values of unsigned short) and, therefore, the result is not well-defined, since an overflow will occur (65535 ^ 2 = 4294836225 > 2147483647), which means that the value of product is not well-defined:

6.3.1.1-1

If an int can represent all values of the original type, the value is converted to an int; otherwise, it is converted to an unsigned int. These are called the integer promotions.(48) All other types are unchanged by the integer promotions.

48) The integer promotions are applied only: as part of the usual arithmetic conversions, to certain argument expressions, to the operands of the unary +, -, and ~ operators, and to both operands of the shift operators, as specified by their respective subclauses.

However, according to the following, the result is well-defined, since computations involving unsigned operands do not overflow:

6.2.5-9

The range of nonnegative values of a signed integer type is a subrange of the corresponding unsigned integer type, and the representation of the same value in each type is the same.(31) A computation involving unsigned operands can never overflow, because a result that cannot be represented by the resulting unsigned integer type is reduced modulo the number that is one greater than the largest value that can be represented by the resulting type.

Does the variable product in the aforementioned statement have a well-defined value?

EDIT: What should happen in the following case?

unsigned short lhs = USHRT_MAX;
unsigned short rhs = USHRT_MAX;
unsigned short product = lhs * rhs;
like image 422
Alexandros Avatar asked Mar 27 '13 10:03

Alexandros


People also ask

What is integer promotion?

Integer promotion is the implicit conversion of a value of any integer type with rank less or equal to rank of int or of a bit field of type _Bool, int, signed int, unsigned int, to the value of type int or unsigned int.

What is type promotion in C?

Introduction. Type promotion in C is a method to convert any variable from one datatype to another. C allows variables of different datatypes to be present in a single expression. There are different types of type conversions available in C. They are Implicit type conversion and Explicit type conversion.


2 Answers

The promotion wins.

Says section 5.2.4.2.1 about the constants USHRT_MAX etc.:

The values given below shall be replaced by constant expressions suitable for use in #if preprocessing directives. Moreover, except for CHAR_BIT and MB_LEN_MAX, the following shall be replaced by expressions that have the same type as would an expression that is an object of the corresponding type converted according to the integer promotions.

So the multiplication is on ints, and involves no unsigned operands, unambiguously, there is no conforming way to implement USHRT_MAX to get an operation involving unsigned operands if USHRT_MAX < INT_MAX. Thus you have overflow, and undefined behaviour.

Regarding the added question

EDIT: What should happen in the following case?

unsigned short lhs = USHRT_MAX;
unsigned short rhs = USHRT_MAX;
unsigned short product = lhs * rhs;

That is exactly the same situation. The operands of * are subject to the integer promotions, all values of type unsigned short can be represented as ints by the assumption on the values of USHRT_MAX and INT_MAX, so the multiplication is on ints, and with the specified values overflows.

You need to convert at least one operand to an unsigned type that is not promoted to int in oerder to have the multiplication be performed on unsigned operands.

like image 178
Daniel Fischer Avatar answered Oct 16 '22 01:10

Daniel Fischer


You get UB since by the time the multiplication operator is applied, its operands are already signed integers (because of the promotions to int occurring first).

You can work-around that with this:

unsigned short product = USHRT_MAX * (unsigned)USHRT_MAX;

Proof that (unsigned)some_integer stays unsigned:

#include <stdio.h>

int main(void)
{
  printf("1u * (-1) = %f\n", (((unsigned)1) * (-1)) + 0.0);
  printf("1 * (-1) = %f\n", (1 * (-1)) + 0.0);
  return 0;
}

Output (ideone):

1u * (-1) = 4294967295.000000
1 * (-1) = -1.000000

Good catch, btw.

like image 42
Alexey Frunze Avatar answered Oct 16 '22 00:10

Alexey Frunze