Introduction
C lets you mix types freely in an expression: char with int, int with unsigned, int with double. Before any arithmetic happens, the compiler silently converts the operands to a common type, following fixed rules. Most of the time the result is what you expect. When signed and unsigned values meet, or when a value is too big for its destination, the result can be startling: in C, -1 < 1u is false.
This note gives the conversion rules in the order the compiler applies them, then the conversions that happen on assignment, in function calls and with casts.
Conventions: C11, LP64 (32-bit int, 64-bit long and size_t), two's complement, IEEE 754 float and double.
Prerequisites
- C Program Structure and Data Types: type sizes and ranges.
- Operators, Precedence and Side Effects: how expressions are grouped.
Core concepts
Implicit and explicit conversions
- An implicit conversion is inserted by the compiler: in arithmetic, in comparisons, on assignment, on passing an argument and on returning a value.
- An explicit conversion is a cast,
(type) expression. A cast is a unary operator with the precedence of the other unary operators, so(double)x / yconverts onlyx, and then the division is done indouble.
Integer conversion rank
Every integer type has a rank: _Bool < char = signed char = unsigned char < short < int < long < long long. An unsigned type has the same rank as its signed counterpart. Rank, not size, drives the rules (although on most systems a higher rank also means at least as many bits).
Step 1: the integer promotions
In arithmetic, any operand whose type has rank below int (char, short, their signed and unsigned versions, _Bool, and bit-fields) is converted to int if int can represent every value of the original type, and to unsigned int otherwise. On every common system int is wider than short, so char and short always promote to int.
Consequences:
unsigned char a = 200, b = 100; int s = a + b;gives 300: the addition is done inint, not in 8 bits.sizeof('a' + 'b')andsizeof(sh + sh)for ashort share bothsizeof(int), which is 4.~and-on a small unsigned type act on the promotedint(Bitwise Operators).
Step 2: the usual arithmetic conversions
For binary operators such as + - * / % < > == & | ^ and ?: (its second and third operands), after the promotions the two operands are brought to one common type:
- If either operand is
long double, convert the other tolong double. Otherwise, if either isdouble, convert todouble. Otherwise, if either isfloat, convert tofloat. - Otherwise both are integers. Apply the integer promotions to both, then:
- 1. If both have the same type, stop.
- 2. If both are signed, or both unsigned, convert the one of lower rank to the type of higher rank.
- 3. If the unsigned operand has rank greater than or equal to the signed one, convert the signed operand to the unsigned type.
- 4. Otherwise, if the signed type can represent every value of the unsigned type, convert the unsigned operand to the signed type.
- 5. Otherwise convert both to the unsigned type corresponding to the signed operand's type.
The shifts << and >> are an exception: they apply only the integer promotions, separately, and the result has the promoted type of the left operand.

Converting a signed value to unsigned
When a value is converted to an unsigned type that cannot hold it, the result is the value reduced modulo (C11 6.3.1.3), where is the width of the unsigned type. For and a 32-bit unsigned int: , the largest unsigned int. That is why:
-1 < 1uis 0. Rule 2.3 converts tounsigned int, giving 4294967295, which is not less than 1.int i = -1; unsigned u = 1; i < uis 0 for the same reason.sizeof(int) > -1is 0.sizeofyieldssize_t(unsigned longon LP64), so becomes .(int)sizeof(int) > -1is 1.unsigned u = 1; u - 2is 4294967295, not .
When the answer depends on the data model
Rule 2.4 makes some results differ between systems. Compare -1L < 1u:
- LP64:
long(64 bits, signed) has higher rank thanunsigned int(32 bits) and can represent all its values. Rule 2.4 converts1utolong; the comparison is , which is 1. - ILP32:
longandunsigned intare both 32 bits.longcannot represent 4294967295, so rule 2.5 converts both tounsigned long; becomes 4294967295 and the result is 0.
Conversions to a narrower or different type
| From | To | Result |
|---|---|---|
| any integer | unsigned type too small | value modulo : well defined |
| any integer | signed type too small | implementation-defined result or an implementation-defined signal (gcc: modulo , so (char)200 is when char is signed) |
| floating | integer | truncated toward zero: (int)3.99 is 3, (int)-3.99 is ; if the truncated value is out of range, undefined |
| integer | floating | exact if representable; otherwise the next higher or next lower representable value, chosen in an implementation-defined way (IEEE round-to-nearest in practice). float f = 16777217; stores 16777216, because needs 25 significant bits and float has 24 |
double | float | rounded if in range; undefined if out of range |
| any scalar | _Bool | 0 if the value compares equal to 0, otherwise 1 |
Signed overflow is not a conversion. INT_MAX + 1 is computed in int and overflows: undefined behaviour. Converting the large value 4294967295 to int is a conversion: implementation-defined. The distinction matters in "what is printed" questions.
Conversions on assignment, calls and returns
- Assignment converts the right operand to the type of the left operand, with the rules in the table above.
unsigned char c = 200 + 100;stores . - Calls to a prototyped function convert each argument to the parameter type, as if by assignment.
- Calls with no prototype, and the variable arguments of
printf, get the default argument promotions: integer promotions, andfloatbecomesdouble. That is why%fprints bothfloatanddouble, and why%dfor acharargument is correct. - Return converts the returned expression to the function's return type.
The arithmetic type is chosen before the result is stored
The type of an expression depends only on its operands, never on where the result goes.
long big = 100000 * 100000;multiplies twoints; does not fit in 32 bits, so this is signed overflow (undefined), even thoughlongcould hold the result. Write100000L * 100000, which gives 10000000000.double avg = sum / n;withint sum = 7, n = 2gives 3.0; the division was integer division.(double)sum / ngives 3.5;(double)(sum / n)gives 3.0 because the cast is applied after the division.
Worked examples
All outputs below are for C11 with gcc on x86-64 Linux (LP64).
Example 1.
unsigned u = 1; int i = -1;
printf("%d %d %d\n", i < u, -1 < 1u, (int)sizeof(int) > -1); /* 0 0 1 */
printf("%d %d\n", sizeof(int) > -1, -1L < 1u); /* 0 1 */Example 2 (promotion then narrowing).
unsigned char a = 200, b = 100;
int s = a + b; /* 300: computed in int */
unsigned char c = a + b; /* 300 mod 256 = 44 */
short sh = 30000;
int t = sh + sh; /* 60000: no short overflow, the sum is an int */Example 3 (floating to integer and casts). With int x = 7, y = 2: (int)3.99 is 3, (int)-3.99 is , (int)(7 / 2.0) is 3; (double)x / y is 3.50, (double)(x / y) is 3.00, x / (double)y is 3.50.
Example 4 (signed narrowing, implementation-defined). On gcc, char ch = (char)200; gives (). Converting (signed char)-1 to unsigned char gives UCHAR_MAX; this is 255 when CHAR_BIT=8. Integer-to-unsigned conversion is modulo one more than the target maximum.
Example 5 (an unsigned loop that never ends).
for (unsigned k = n; k >= 0; k--) /* k >= 0 is always true */
...When k is 0, k-- makes it 4294967295 and the loop continues. gcc warns "comparison of unsigned expression in '>= 0' is always true". The same trap appears with strlen(s) - 1 when s is empty: strlen returns size_t, so the result is , not .
Important properties and observations
- Arithmetic is never done in a type narrower than
int. - A signed operand meeting an unsigned operand of the same or higher rank becomes unsigned; negative values become huge.
- Whether
longversusunsigned intcomparisons are signed or unsigned depends on the data model. - Floating to integer truncates toward zero; out of range is undefined.
- Integer to
floatcan lose precision above ; integer todoubleabove . - The destination of an assignment does not influence the type in which the right side is computed.
- Narrowing to unsigned is always well defined; narrowing to signed is implementation-defined; signed overflow during arithmetic is undefined.
GATE examination perspective
- Signed versus unsigned comparison is a trap:
if (-1 < sizeof(int))takes the false branch. - Character arithmetic. Assuming an ASCII-compatible execution character set,
char c = 'A' + 1;is'B', andprintf("%d", c)prints 66. ISO C does not guarantee consecutive letter codes or ASCII numeric values. - Overflow of small types. For integer-to-integer conversion, 300 becomes in
unsigned char, and -1 becomesUCHAR_MAX. The values 44 and 255 respectively requireCHAR_BIT=8(UCHAR_MAX=255); they are not universal implementation values. Assigning it to asigned chargives an implementation-defined result; gcc reduces modulo 256, giving 44 for 300 and for 200. - Integer division before conversion.
float f = 5 / 2;stores 2.0. - Data model questions. When a question gives sizes, apply rules 2.3 to 2.5 with those sizes.
Summary
Sources
- ISO/IEC 9899:2011 (C11), committee draft N1570, (consulted 2026-11-09), Section 6.3.1.1 (integer rank and promotions), 6.3.1.3 (signed and unsigned integers), 6.3.1.4 (real floating and integer), 6.3.1.8 (usual arithmetic conversions), 6.5.2.2 (function calls, default argument promotions), 6.5.4 (cast operators), 6.5.7 (shift operators).
- B. W. Kernighan and D. M. Ritchie, The C Programming Language, 2nd ed., Prentice Hall, 1988: Section 2.7 (type conversions).