I understand the implicit conversions of the C language between integer and floating point types, but I have a question for signed/unsigned implicit type conversions.
If you add, for example, an unsigned char and a signed int, what will be the resulting type? Would it be an unsigned int, a signed int, or something else?
I don't see anything specific in the C99 ANSI standard about this, so any help is appreciated.
In C99, the reference is 6.3.1.8 "Usual arithmetic conversions".
Many operators that expect operands of arithmetic type cause conversions and yield result
types in a similar way. The purpose is to determine a common real type for the operands
and result. For the specified operands, each operand is converted, without change of type
domain, to a type whose corresponding real type is the common real type. Unless
explicitly stated otherwise, the common real type is also the corresponding real type of
the result, whose type domain is the type domain of the operands if they are the same,
and complex otherwise. This pattern is called the usual arithmetic conversions:
First, if the corresponding real type of either operand is long double, the other
operand is converted, without change of type domain, to a type whose
corresponding real type is long double.
Otherwise, if the corresponding real type of either operand is double, the other
operand is converted, without change of type domain, to a type whose
corresponding real type is double.
Otherwise, if the corresponding real type of either operand is float, the other
operand is converted, without change of type domain, to a type whose
corresponding real type is float. 51)
Otherwise, the integer promotions are performed on both operands. Then the
following rules are applied to the promoted operands:
If both operands have the same type, then no further conversion is needed.
Otherwise, if both operands have signed integer types or both have unsigned
integer types, the operand with the type of lesser integer conversion rank is
converted to the type of the operand with greater rank.
Otherwise, if the operand that has unsigned integer type has rank greater or
equal to the rank of the type of the other operand, then the operand with
signed integer type is converted to the type of the operand with unsigned
integer type.
Otherwise, if the type of the operand with signed integer type can represent
all of the values of the type of the operand with unsigned integer type, then
the operand with unsigned integer type is converted to the type of the
operand with signed integer type.
Otherwise, both operands are converted to the unsigned integer type
corresponding to the type of the operand with signed integer type.
Addition performs the usual arithmetic conversions, so, when adding unsigned char and signed int, either:
first the unsigned char is promoted to int, and then both types are the same, so the result has type int, or
(uncommon) int cannot represent all possible unsigned char values. In this case, unsigned char is promoted to unsigned int, and the third sub-bullet applies: unsigned int has equal rank to int, so the int operand is converted to unsigned int, and the result has type unsigned int.
It will almost certainly be a signed int and it depends on the system where the code runs. Check the paragraph Integral promotion here
unsigned char or unsigned short can be converted to int if it can hold
its entire value range, and unsigned int otherwise.
On a POSIX system for example it will definitely be a signed int as a char is always 8bits and an int is at least 16bits. Therefore an int can represent every possible value of an unsigned char. There are apparently systems where char is more that 8bits.
Related
In C, I understand type conversions, integer promotion, casting, etc. for standard types, but how do the stdint.h types factor into this?
For type rankings, the rules state:
No two signed integer types shall have the same rank, even if they have the same representation.
The rank of any unsigned integer type shall equal the rank of the corresponding signed integer type, if any.
So assuming an int is 32 bits, does this mean int > int32_t = uint32_t > short int in the rankings?
Also, are the stdint.h types also subject to integer promotion? For example if I try to add a signed char to a uint32_t, they will both get promoted to unsigned ints?
To answer your first question: no. Since int32_t is usually defined with a typedef like this
typedef int int32_t;
it is the same as int and will have the same rank as int.
To answer the second question: yes. Integer promotion still applies. The types defined in stdint.h behave just like the types they are aliases of.
By the way, to be more confident in how your compiler behaves, you can test all of these things in your compiler by writing invalid code like this and carefully looking at the error message, which will (if you have a good compiler) reveal the type of the expression on the right hand side:
void * x = (signed char)-1 + (uint32_t)0;
According to the C Standard
— The rank of any standard integer type shall be greater than the rank
of any extended integer type with the same width.
The exact integer types for 2's complement representation are defined as tyoedef aliases of standard integer types.
From the C Standard (7.20.1.1 Exact-width integer types)
...and (for the signed types) that have a two’s complement
representation, it shall define the corresponding typedef names.
So this relational when the type int has 32 bits (for 2's complement representation)
int > int32_t = uint32_t > short int
is correct except that the relation int > int32_t provided that the type int32_t is an alias name for the type int introduced by a typedef declaration..
Also, are the stdint.h types also subject to integer promotion? For
example if I try to add a signed char to a uint32_t, they will both
get promoted to unsigned ints?
Here the object of the type unsigned char is promoted to the type int and the object of the type uint32_t is promoted to the type unsigned int (provided that int has 32-bits) due to the integer promotions
From the C Standard
If an int can represent all values of the original type (as restricted
by the width, for a bit-field), the value is converted to an int;
otherwise, it is converted to an unsigned int. These are called the
integer promotions. 58) All other types are unchanged by the integer
promotions.
And then the object of the type int is converted to the type unsigned int due to the usual arithmetic conversions.
From the C Standard (6.3.1.8 Usual arithmetic conversions)
Otherwise, both operands are converted to the unsigned integer type
corresponding to the type of the operand with signed integer type.
Pay attention to then the name uint32_t can be an alias for the type unsigned int introduced by a typedef declaration. In this case uint32_t is the same type as unsigned int.
According to C17 6.3.1.1
If an int can represent all values of the original type (as restricted by the width, for a bit-field), the value is converted to an int; otherwise, it is converted to an unsigned int.
Does 'all values' mean whole identifiers in same scope?
Then when some identifiers are with variable which can't be represented by int (such as long long int) in same identifier scope, there are no any promotion for identifiers?
Does 'all values' mean whole identifiers in same scope?
I'm not sure why you think all identifiers in a scope matter. The integer promotion applies to a single item, be it an object (e.g., variable) or expression.
What it's saying is that, if every possible value of this item (already one of the types guaranteed to fit into an int or unsigned int as per the paragraphs before your quote(a)) can be represented by the int type, it gets promoted to an int. Otherwise it gets promoted to an unsigned int.
(a) The text states, in full:
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 (other than int or unsigned int) 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 (as restricted by the width, for a bit-field), the value is converted to an int; otherwise, it is converted to an unsigned int. These are called the integer promotions. All other types are unchanged by the integer promotions.
I'm reading Standard N2310 about enumerators and faced some misunderstanding.
6.7.2.2/3:
The identifiers in an enumerator list are declared as constants that
have type int and may appear wherever such are permitted.
Also
6.7.2.2/4:
Each enumerated type shall be compatible with char, a signed integer
type, or an unsigned integer type.
But the definition of signed integer type and unsigned integer type allows integer types wider then int. Actually:
6.2.5/4:
There are five standard signed integer types, designated as
signed char, short int, int, long int, and long long int. (These and other
types may be designated in several additional ways, as described in
6.7.2.) There may also be implementation-defined extended signed integer types. 38) The standard and extended signed integer types are
collectively called signed integer types. 39)
So it is legitimate for a compiler to choose long long int or char as a representation for an enumerated type even though enumerators always have type int. Also integer promotion always converts to int. So if implementation chooses long long int or char for a enumeration type representation we will have assignment of incompatible types. Something like this:
//char is wide enough to hold all the values, let's choose it
enum something{
a = 0,
b
};
enum something sth = a; //a has type int, but enum something is incompatible with int
//UB?
Should we do an explicit cast to stay strictly conforming?
enum something sth = (enum something) a;
I have a program which uses multiple different int types.
Most often used are uint64_t and the standard int. However I wonder if I can safely do operations mixed between them.
For instance I have an uint64_t and I want to add an int to it and store that value as another uint64_t.
Is doing such a thing safe? Do I have to cast the int to uint64_t before I can use operations on it?
I can`t really find stuff about it online. It might just be allowed and no one questions it or my Google queries are wrong.
Anyway so basically my question is can I mix and do operations with different types of ints?
Yes you can.
Your compiler will take care of the conversions. The only thing to worry about is overflow - if you store the result in a container that is smaller than the inputs.
The Google search term you need is "implicit type conversions" - see for example http://pic.dhe.ibm.com/infocenter/ratdevz/v8r5/index.jsp?topic=%2Fcom.ibm.tpf.toolkit.compilers.doc%2Fref%2Flangref_os390%2Fcbclr21011.htm
That link includes the following table:
Arithmetic conversion proceeds in the following order:
Operand Type Conversion
---------------------------------------------+--------------------------------------------
One operand has long double type | The other operand is converted to long double type.
---------------------------------------------+--------------------------------------------
One operand has double type | The other operand is converted to double.
---------------------------------------------+--------------------------------------------
One operand has float type | The other operand is converted to float.
---------------------------------------------+--------------------------------------------
One operand has unsigned long long int type | The other operand is converted to unsigned long long int.
---------------------------------------------+--------------------------------------------
One operand has long long int type | The other operand is converted to long long int.
---------------------------------------------+--------------------------------------------
One operand has unsigned long int type | The other operand is converted to unsigned long int.
---------------------------------------------+--------------------------------------------
One operand has unsigned int type |
and the other operand has long int type |
and the value of the unsigned int can be |
represented in a long int | The operand with unsigned int type is converted to long int.
---------------------------------------------+--------------------------------------------
One operand has unsigned int type |
and the other operand has long int type |
and the value of the unsigned int cannot be |
represented in a long int | Both operands are converted to unsigned long int
---------------------------------------------+--------------------------------------------
One operand has long int type | The other operand is converted to long int.
---------------------------------------------+--------------------------------------------
One operand has unsigned int type | The other operand is converted to unsigned int.
---------------------------------------------+--------------------------------------------
Both operands have int type | The result is type int.
---------------------------------------------+--------------------------------------------
C standard says,
If the operand that has unsigned integer type has rank greater or equal to the rank of the type of the other operand, then the operand with signed integer type is converted to the type of the operand with unsigned integer type.
Therefore as int & unsigned int are of same rank you can add them, and when you add them int is converted to unsigned int leaving the result again in to unsigned int.
From the C standard
6.3.1.1
If an int can represent all values of the original type (as restricted
by the width, for a bit-field), the value is converted to an int;
otherwise, it is converted to an unsigned int. These are called the
integer promotions.
So this means that if I use a long int in an expression it will be downgraded to an unsigned int?
The bit you quoted is restricted by the text above it:
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 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. All other types are unchanged
by the integer promotions.
In other words, long int doesn't get promoted to int or unsigned int.
I think "original type" refers to "[...] an integer type (other than int or unsigned int)
whose integer conversion rank is less than or equal to the rank of int and
unsigned int", as defined earlier in section 6.3.1.1.2. But, nice try :)