integer promotion and arithmetic conversion

S

sarathy

Hi,
What is integer promotion? How is it different from
arithmetic conversion?

Regards,
Sarathy
 
A

Ark

sarathy said:
Hi,
What is integer promotion? How is it different from
arithmetic conversion?

Regards,
Sarathy
AFAIK, integer promotion refers specifically to integer types
participating in a binary operation (read: sub-expression) where the
types of operands must be identical (+ - * / % | ^ &).
E.g.,
char c;
short s;
unsigned u;
long l;
If I am not mistaken in this wee hour;
u+l means (long)u+l even if sizeof(unsigned)==sizeof(long)
s+u means (unsigned)s+u even if sizeof(unsigned)==sizeof(short)
c+s means (int)c+(int)s
etc.
signed promotion is value-preserving; promotion to an unsigned type
preserves the bit pattern.
Additionally, everything in an expression is promoted to at least int:
c+s means (int)c+(int)s
(I believe, in the old days it was not NECESSARILY true and was
implementation-defined.) E.g., on a typical 32-bit machine
unsigned short s = 0x1000U;
......
s<<4 means ((int)s)<<4, i.e., 0x10000, not a short value.

These rules are made so that C programmers capable of writing robust
arithmetic expressions (or better yet, portable ones) could command big
bucks :)
 
M

Michael Mair

sarathy said:
Hi,
What is integer promotion? How is it different from
arithmetic conversion?

I just gave a very loose explanation in
<[email protected]>
As this is IMO one of the topics that are presented rather
readable in the standard text, I suggest that you have a look
at "n1124.pdf" (just google for it), section 6.3.
Note that this is the current C99 standard plus technical
corrigenda -- C90 did behave a little bit different when it
came to the usual arithmetic conversions for operations involving
floating point operands and had no long long type.

Cheers
Michael
 
P

pete

Ark said:
AFAIK, integer promotion refers specifically to integer types
participating in a binary operation (read: sub-expression) where the
types of operands must be identical (+ - * / % | ^ &).
E.g.,
char c;
short s;
unsigned u;
long l;
If I am not mistaken in this wee hour;
u+l means (long)u+l even if sizeof(unsigned)==sizeof(long)

That's not what integer promotion is.
Integer promotion can only result in an expression
of either type int or type unsigned.

sizeof(c + c) == sizeof(u)
 
A

Ark

pete said:
That's not what integer promotion is.
Integer promotion can only result in an expression
of either type int or type unsigned.

sizeof(c + c) == sizeof(u)
You are correct of course:
6.3.1.1
The following may be used in an expression wherever an int or unsigned
int may be used:
— An object or expression with an integer type whose integer conversion
rank is less than or equal to the rank of int and unsigned int.
— A bit-field of type _Bool, int, signed int, or unsigned int.
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.
 
C

Chris Torek

char c;
short s;
unsigned u;
long l;
If I am not mistaken in this wee hour;
u+l means (long)u+l even if sizeof(unsigned)==sizeof(long)
s+u means (unsigned)s+u even if sizeof(unsigned)==sizeof(short)
c+s means (int)c+(int)s

Alas, you are mistaken. The ANSI/ISO rules are terribly complicated
by depending on the relative values of the corresponding MAXes.

Consider the "u+l" case for a moment. Suppose we are on a typical
32-bit machine, with 32-bit int and 32-bit long, and hence both
INT_MAX and LONG_MAX are 2147483647 while UINT_MAX is 4294967295.

The ANSI "value-preserving" rule says that widening a narrower
signed type produces a new, wider signed type if and only if the
relative *_MAX is not exceeded. Otherwise, it produces a new,
wider unsigned type.

Since UINT_MAX exceeds LONG_MAX, widen(u) produces unsigned long,
instead of signed long. We then have (unsigned long) + (signed long);
the second "signed" long is converted to "unsigned", and the addition
is done with unsigned arithmetic -- where overflow is discarded --
and the result has type "unsigned long".

If LONG_MAX is greater than UINT_MAX, however, widen(u) produces
signed long. We then have (signed long)+(signed long); the addition
is done in signed arithmetic (overflow giving undefined behavior)
and the result has type "signed long". This is the case on a
typical 64-bit machine (32-bit int, 64-bit long).

(See also the appended test code.)
signed promotion is value-preserving;

This is correct: it preserves the value if possible (if the new
wider type has a lower *_MAX, we hit a snag; but this case is either
completely ruled out by the Standard, or else at least never seems
to occur in practice; I am not sure which). (C89 and C99 have
somewhat different wording, in part because C99 now has "long long"
as well.)
promotion to an unsigned type preserves the bit pattern.

This is only true for two's complement systems. On a Univac, you
get some interesting code.

[test code]
#include <stdio.h>

long l;
unsigned u;

void f0(void) { if (u + l > 0) puts("u + l is unsigned"); }
void f1(void) { if ((long)u + l > 0) puts("(long)u + l is unsigned"); }
void f2(void) { if (u + (unsigned)l > 0) puts("u + (unsigned)l is unsigned"); }

int main(void) {
u = -1U, l = -1L; /* -1L + -1L = -2L */
f0();
f1();
f2();
return 0;
}
 
E

ena8t8si

Chris said:
Alas, you are mistaken. The ANSI/ISO rules are terribly complicated
by depending on the relative values of the corresponding MAXes.

Consider the "u+l" case for a moment. Suppose we are on a typical
32-bit machine, with 32-bit int and 32-bit long, and hence both
INT_MAX and LONG_MAX are 2147483647 while UINT_MAX is 4294967295.

The ANSI "value-preserving" rule says that widening a narrower
signed type produces a new, wider signed type if and only if the
relative *_MAX is not exceeded. Otherwise, it produces a new,
wider unsigned type.

Since UINT_MAX exceeds LONG_MAX, widen(u) produces unsigned long,
instead of signed long. We then have (unsigned long) + (signed long);
the second "signed" long is converted to "unsigned", and the addition
is done with unsigned arithmetic -- where overflow is discarded --
and the result has type "unsigned long".

If LONG_MAX is greater than UINT_MAX, however, widen(u) produces
signed long. We then have (signed long)+(signed long); the addition
is done in signed arithmetic (overflow giving undefined behavior)
and the result has type "signed long". This is the case on a
typical 64-bit machine (32-bit int, 64-bit long).

This explanation is quite good, but I have a quibble.
For promotions, there is a notion of widening. For
binary operators though, both operand types participate
in finding a common real type and both operand values
are converted to that type. It isn't that we "widen"
one type and convert the other - both operands are
simply converted (and indeed no widening may take
place even when the conversion ranks are different).

Don't get me wrong, I like the explanation; I just
think it could be better if couched in language
more closely parallel to how language in the Standard
expresses it.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,581
Members
45,057
Latest member
KetoBeezACVGummies

Latest Threads

Top