Type Casting in C

Type casting in C language is a fundamental technique that allows forcing the conversion of a variable from one data type to another. This operation is particularly useful when you want to have precise control over type conversions, avoiding undesired behaviors that might occur with implicit conversions. Explicit casting allows the programmer to clearly indicate to the compiler how to handle the data, improving code readability and maintenance.

In this lesson, we will explore in detail how type casting works in C, when it is useful to use it and what its practical applications are

Explicit Type Conversion: Casting

In the previous lesson we saw how C language handles implicit conversion of arithmetic types. We studied its rules and saw when they apply.

Often, however, we need greater control over type conversion. For these cases, C language provides explicit type conversion which is more commonly called casting.

To perform an explicit cast from one type to another, the following syntax is used:

(type) expression;

Where, type is the data type to which we want to convert the expression and expression is the expression we want to convert.

Let's take an example. Suppose we want to calculate the fractional part of a floating-point number. We can do it very simply by performing an explicit cast:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
#include <stdio.h>

int main() {
    float number;
    float fractional_part;

    printf("Enter a floating-point number: ");
    scanf("%f", &number);

    fractional_part = number - (int) number;

    printf("The fractional part of %.2f is %.2f\n", number, fractional_part);

    return 0;
}

We applied the cast at line 10 of the example:

fractional_part = number - (int) number;

In this case, we first converted number to integer through the sub-expression:

(int) number

In this way, we forced the conversion of number to an integer, thus losing the fractional part.

The subsequent operation includes the subtraction between number, which is a float, and (int) number which is an integer. Based on the implicit conversion rules we saw in the previous lesson, before performing the subtraction, the compiler converts the integer (int) number to a float and then executes the subtraction.

For clarity, let's see with a numerical example. Suppose we entered the number 3.14. The variable number will be worth 3.14F. So:

  1. (int) number will be worth 3. The fractional part is lost in the explicit cast to int;
  2. number - (int) number is a subtraction between a float and an int. So (int) number is converted from 3 to 3.0F;
  3. The subtraction 3.14F - 3.0F is executed and the result is 0.14F. This is the value of the fractional part.

Recapping:

Definition

Type Casting in C Language

Type Casting, or Explicit Type Conversion, is an operation that allows forcing the conversion of one data type to another.

The syntax to convert one data type to another is the following:

(type) expression;

Where type is the data type to which we want to convert the expression and expression is the expression we want to convert.

Uses of Casting

In C language, casting is essentially used for two purposes:

  1. To document a conversion operation that might be performed by the compiler anyway;

    For example, if we want to convert a float to a double, we can write:

    float f;
    
    /* ... */
    
    double d = (double) f;
    

    Even though the compiler would have performed the implicit conversion, the explicit cast documents the programmer's intention.

    In other cases, the compiler avoids showing a warning. For example:

    double d;
    
    /* ... */
    
    float f = (float) d;
    

    In this case the conversion from double to float might cause a loss of precision. In the absence of explicit casting, the compiler would have performed the conversion anyway, but would have shown a warning.

    Through casting, we are essentially telling the compiler that this is an intended behavior and not an oversight.

  2. To force a conversion that the compiler would not perform;

    Let's take a simple example:

    float quotient;
    
    int dividend = 15;
    int divisor = 4;
    
    quotient = dividend / divisor;
    

    Reading this code, at first glance one might think that the final result, namely the value of the quotient variable, is 3.75 since quotient is a float. But this is not the case.

    In fact, the compiler, first of all, divides dividend by divisor. But, since the latter two are int, it performs the division between int which returns an int and truncates the result to 3. Only subsequently, during assignment, the result is converted to float. But it will be too late, since the result will already be truncated and quotient will be worth 3.0.

    In this situation casting comes to our aid. To obtain the correct result, 3.75 we must force the compiler to convert dividend or divisor to float before performing the division:

    quotient = (float) dividend / divisor;
    

    Note that we only cast dividend. This is sufficient since, being divisor an int, the compiler will perform the implicit conversion of divisor to float before executing the division.

  3. Avoid cases of overflow.

    In some cases, explicit casting is necessary to avoid overflow conditions.

    Let's take an example:

    long int x;
    int y = 1000000;
    
    x = y * y;
    

    At first glance, we might think that the above code is correct. But this is not the case. The result of the multiplication y * y is 1000000000000 which can be stored in a long int but not in an int.

    However, the multiplication between y and itself is a multiplication between int that returns an int. Only subsequently, during assignment, the result is converted to long int. But it will be too late, since the result will have already overflowed returning an incorrect result: -727379968.

    We can avoid this problem by using explicit casting on one of the two operands:

    x = ((long) y) * y;
    

    In this way, the first operand of the multiplication is converted to long before performing the multiplication. It is not necessary to explicitly convert the second operand since the compiler will perform the implicit conversion to long before executing the multiplication.

    The result will be correct and x will be worth 1000000000.

Casting and Precedence Rules

Previously we studied the rules that determine the evaluation precedence of operators in expressions.

In this general scheme, what is the precedence of casting compared to other operators?

C language considers the casting operation, (type), as a unary operator. This means that casting has the same precedence as other unary operators, and therefore has higher precedence than binary operators.

So, returning to the division example we have that the expression:

quotient = (float) dividend / divisor;

is evaluated as:

quotient = ((float) dividend) / divisor;

We could have obtained the same result with the following expression:

quotient = dividend / (float) divisor;

Or, more explicitly:

quotient = (float) dividend / (float) divisor;

In all cases, the result is the same. The casting is performed before the division.

Obviously, in cases where you are not sure about precedence or to make the code more readable, it is always possible to use parentheses to force the evaluation order.

Definition

Casting and Precedence Rules

Casting is considered a unary operator and has the same precedence as other unary operators. Therefore, it has higher precedence than binary operators.

To force the evaluation order, it is possible to use parentheses.

In Summary

Type casting is an operation that allows forcing the conversion of one data type to another. This operation is very useful to control type conversion and to avoid undesired behaviors.

We saw in this lesson that:

  • Type casting is performed through the syntax (type) expression;;
  • Casting is a unary operator and has the same precedence as other unary operators;
  • Casting is used to document a conversion operation, to force a conversion that the compiler would not perform and to avoid cases of overflow.