CCS News

Type Conversions

Monday 12 June, 2023

In order to evaluate expressions the C compiler uses a complex set of rules to get a result that is consistent and as intuitive as possible. Sometimes the coder needs to add an explicit typecast in order to nudge the compiler to do something specific.

To understand this topic we will start with a simple example that will show a possible problem:

int8 x,y;
int16 z;


z = x * y;

There are two expression evaluations here. One is the * and the second is the =. The order is *, and then =, and that is controlled by operator precedence, another topic. The two operands for the * are eight bit so the eight bit multiply is used, yielding an eight bit result. In this case the result of the multiply will be 244. Then because the = operator has an 8 bit operand and a 16 bit operand, the 8 bit operand is automatically converted (Integral Promotion because of Usual Arithmetic Conversion) to 16 bit by the compiler. Then the assignment is done.

Probably the coder expected a 500 in z, not 244. To fix the situation we can do a typecast on either 8 bit operand like this:

z = (int16)x * y;

Now when the multiply is done the second eight bit operand is converted to 16 bit to match the other operand and the multiply and result are now 16 bit (500).

Note that this concept is not an invention of the CCS C compiler although people who have never had this trouble with another compiler might think it is. A C compiler for a PC for example where an int is 32 bit by default may never cause a problem because the numbers used are much smaller than the default type. Because the CCS C compiler starts with an eight bit int by default, problems are more common.

Having said that, because the rules are somewhat complex and not always fully understood and sometimes not very specific in the specification, the CCS C compiler has tweaked the rules over the years. Sometimes to match the latest interpretation of the spec or sometimes to match a GNU compiler or the Microchip compiler to help in code portability.

Explicit Conversion, or Explicit Cast, or Explicit Typecast, or Tyepcast:
This is where the coder has a typecast that will force a conversion from one type to another. The syntax is (new type)expression and if the new type is compatible the value will be the same. That is to say this is more than a bit for bit movement, for example a float to an integer will have the same value to the extent possible.

Implicit Conversion, or Automatic Conversion:
This is where the compiler uses a set of rules to be detailed later to perform a conversion.

Type Conversion:
Either an Explicit Conversion or Implicit Conversion.

Integral Promotion:
In this case any type conversion where a smaller integer is converted to a larger one (or superior one). The value does not change when promoted.

Usual Arithmetic Conversions:
These are implicit conversions that are made according to the rules when an operator and operands are involved. One operand is converted based on the type of the other operand BEFORE the operator is invoked.

The Rules:
The way to read this table is for each group start at the top and go down until you find the first rule that applies. Use that result and stop.

Be aware that by default the CCS C compiler for 24 bit parts is signed and the size is 16 bits. For all other compilers the default is unsigned and the size is 8 bits. This can be changed using #type so this needs to be taken into account when considering the rules.

The syntax in this table is not real C, just pseudo-code that a C coder should be able to understand.

The table as follows:
Integer Constant Types
Leading 0 in constantUnsigned
Trailing u in constantUnsigned
Else if default type is signed || is preceded by a minus
Else if default type is unsigned
Find the smallest of these types the constant will fit intoint8, int16, int32, int48, int64
Integral promotion anytime X is used in an expression (int is the default type)
issigned (X) && bitsin(X)<bitsin(int) && (X>max(signed(X))X=>unsigned int
Any type conversion from signed to unsigned
bitsin(signed(X)) <=bitsin(unsigned(X))X+>unsigned(X) sign extend
X+>unsigned(X) truncate
Usual Arithmetic Conversions for Binary operations
isfloat(X) && !isfloat(Y)Y=>typeof(X)
isfloat(X) && bitsin(X)>bitsin(Y)Y=>typeof(X)
Only if both operands are integer:
bitsin(X)>bitsin(Y) && (X & Y are both signed or unsigned)Y=>typeof(X)
bitsin(X)>bitsin(Y) && unsigned(X) && signed(Y)Y=>typeof(X)
bitsin(X)>bitsin(Y) && signed(X) && unsigned(Y)Y=>typeof(X)
bitsin(X) = bitsin(Y) && unsigned(X) && signed(Y)Y=>unsigned(Y)