Search

Meta

4.4 — Implicit type conversion (coercion)

Previously, you learned that a value of a variable is stored as a sequence of bits, and the data type of the variable tells the compiler how to interpret those bits into meaningful values. Different data types may represent the “same” number differently -- for example, the integer value 3 and the float value 3.0 are stored as completely different binary patterns.

So what happens when we do something like this?

In such a case, the compiler can’t just copy the bits representing the value 3 into float f. Instead, it needs to convert the integer 3 to a floating point number, which can then be assigned to variable f.

The process of converting a value from one data type to another is called a type conversion. Type conversions can happen in many different cases:

Assigning to or initializing a variable with a value of a different data type:

Passing a value to a function where the function parameter is of a different data type:

Returning a value from a function where the function return type is of a different data type:

Using a binary operator with operands of different types:

In all of these cases (and quite a few others), C++ will use type conversion to convert data from one type to another.

There are two basic types of type conversion: implicit type conversion, where the compiler automatically transforms one fundamental data type into another, and explicit type conversions, where the developer uses a casting operator to direct the conversion.

We’ll cover implicit type conversion in this lesson, and explicit type conversion in the next.

Implicit type conversion

Implicit type conversion (also called automatic type conversion or coercion) is performed whenever one fundamental data type is expected, but a different fundamental data type is supplied, and the user does not explicitly tell the compiler how to perform this conversion (via a cast).

All of the above examples are cases where implicit type conversion will be used.

There are two basic types of implicit type conversion: promotions and conversions.

Numeric promotion

Whenever a value from one type is converted into a value of a larger similar data type, this is called a numeric promotion (or widening, though this term is usually reserved for integers). For example, an int can be widened into a long, or a float promoted into a double:

While the term “numeric promotion” covers any type of promotion, there are two other terms with specific meanings in C++:

  • Integral promotion involves the conversion of integer types narrower than int (which includes bool, char, unsigned char, signed char, unsigned short, signed short) to an integer (if possible) or an unsigned int.
  • Floating point promotion involves the conversion of a float to a double.

Integral promotion and floating point promotion are used in specific cases to convert smaller data types to int/unsigned int or double, because those data types are generally the most performant to perform operations on.

The important thing to remember about promotions is that they are always safe, and no data loss will result.

Numeric conversions

When we convert a value from a larger type to a similar smaller type, or between different types, this is called a numeric conversion. For example:

Unlike promotions, which are always safe, conversions may or may not result in a loss of data. Because of this, any code that does an implicit conversion will often cause the compiler to issue a warning.

The rules for conversions are complicated and numerous, so we’ll just cover the common cases here.

In all cases, converting a value into a type that doesn’t have a large enough range to support the value will lead to unexpected results. This should be avoided. For example:

In this example, we’ve assigned a large integer to a char (that has range -128 to 127). This causes the char to overflow, and produces the result:

48

However, converting from a larger integral or floating point type to a smaller similar type will generally work so long as the value fits in the range of the smaller type. For example:

This produces the expected result:

2
0.1234

In the case of floating point values, some rounding may occur due to a loss of precision in the smaller type. For example:

In this case, we see a loss of precision because the float can’t hold as much precision as a double:

0.123456791

Converting from an integer to a floating point number generally works as long as the value fits. For example:

This produces the expected result:

10

Converting from a floating point to an integer works as long as the value fits, but any fractional values are lost. For example:

In this example, the fractional value (.5) is lost, leaving the following result:

3

Evaluating arithmetic expressions

When evaluating expressions, the compiler breaks each expression down into individual subexpressions. The arithmetic operators require their operands to be of the same type. If operands of mixed types are used, the compiler will implicitly convert one operand to agree with the other using a process called usual arithmetic conversion. To do this, it uses the following rules:

  • If the operand is an integer, it undergoes integral promotion (as described above).
  • If the operands still do not match, then the compiler finds the highest priority operand and converts the other operand to match.

The priority of operands is as follows:

  • long double (highest)
  • double
  • float
  • unsigned long long
  • long long
  • unsigned long
  • long
  • unsigned int
  • int (lowest)

We can see the usual arithmetic conversion take place via use of the typeid() operator, which can be used to show the resulting type of an expression.

In the following example, we add two shorts:

Because shorts are integers, they undergo integral promotion to ints before being added. The result of adding two ints is an int, as you would expect:

int 9

Let’s take a look at another case:

In this case, the short undergoes integral promotion to an int. However, the int and double still do not match. Since double is higher on the hierarchy of types, the integer 2 gets converted to the double 2.0, and the doubles are added to produce a double result.

double 6.0

This hierarchy can cause some interesting issues. For example, take a look at the following code:

you might expect the expression 5u - 10 to evaluate to -5 since 5 - 10 = -5. But here’s what actually happens:

4294967291

In this case, the signed integer (10) is promoted to an unsigned integer (which has higher priority), and the expression is evaluated as an unsigned int. Overflow results, and we get an answer we don’t expect.

This is one of many good reasons to avoid unsigned integers in general.

Warning: Microsoft’s Visual C++ 2005 does not seem to issue warnings for unsafe signed/unsigned conversions.

4.4a -- Explicit type conversion (casting)
Index
4.3a -- Namespaces

20 comments to 4.4 — Implicit type conversion (coercion)

  • Allen01

    When I build and compile the code below, the output is -5. You mention that:

    “This hierarchy can cause some interesting issues. For example, you might expect the expression 5u - 10 to evalute to -5 (5u means 5 as an unsigned integer). But in this case, the signed integer (10) is promoted to an unsigned integer, and the result of this expression is the unsigned integer 4294967291!”

    Is the hierarchy issue compiler specific? I am using VC 2005 Express.

    #include “stdafx.h”
    #include 
    
    int main()
    {
    using namespace std;
    int x;
    x = 5u - 10;
    cout < < x;
    return 0;
    }
    
    • The issue is in how you’ve written your program. This is a tricky one.

      For example, if you do this:

      You get 4294967291.

      If you do this:

      You get -5.

      So what’s the difference? The answer has to do with the way types are promoted. When the compiler encounters 5 - 10u, it promotes 5 to an unsigned value, and 5u - 10u produces the result 4294967291 (unsigned). However, it’s worth noting that 4294967291u and -5 have the exact same bit pattern -- the interpretation depends entirely on whether the value is treated as signed or unsigned.

      Because 5 - 10u produces an unsigned value, cout treats it as an unsigned value, and prints 4294967291 as the result.

      However, in your case, you’ve assigned this unsigned value back to a signed integer. Because x is signed, when you print x in the next statement, cout prints the value as if it were signed, which is why it prints -5.

      So the ultimate answer is that the statement as written is true. You’ve simply cast 4294967291 unsigned back to a signed integer and printed that value, which is the value you were intuitively expecting anyway. :)

  • i have a problem i want to do get input from user then i want to do the multiplication,division,subtraction,addititon,and modulus by using type double.How i will do modulus with double variable any one can provide me the code

  • csvan

    I am not entirely clear on the second-last example of this page: About half-way through the tutorial, it is explained that since double has higher precedence then int, an arithmetic operation on an int and a double will cause the int to be turned into a double. Yet, here (the second last example), it is stated that the compiler will complain about a double being converted to an int!

    Is there thus a difference between first EXPLICITLY declaring a variable of a certain type and then using that variable in an arithmetic operation with an implicitly defined variable (such as 2.5), as opposed to have two implicitly declared and defined variables in an arithmetic operation (such as 2 / 2.5)?

    • jdurham

      when promoting an int to a double there is no warning because the int is increasing in precedence (changing a 2 to a 2.0 does not cause any unwanted behavior).

      The compiler will warn you about lowering the precedence of a value, which happens when you convert a double to an int. (Chaning 2.5 into an int results in 2, so the .5 gets lost)

  • Zak

    After doing:

    It printed out 0.

    After doing:

    It printed out 48.

    So what does the number 48 convert to as a character? Why did it come out as 0? Is that the character it was assigned?

    • TBM

      Simply put yes a decimal of 48 converts to a binary of 0011 0000 which is = to a hex value of 30 which evaluates to 0 as a char. Incrementing nValue7 to 49 would return 1 and 50 would return 2 and so on. Remember that an int number and a char number are not necessarily the same binary value.

    • Alex

      Your first example prints the ASCII character assigned to code point 48, which is the character ‘0’. Your second example printed the char as an integer, which prints the numeric value 48.

  • x = static_cast(stuff);

    the type inside is what you want it to be converted TO?

  • Mkc

    Thank you! I read about this in a book a while back and just didn’t get the point. Now I do!

    Your real world examples and “new programmers tend to do this” advice make this a fantastic tutorial.

  • SamHocevar

    I would like to point out that “int nValue = 10 * 2.7″ does not really work “as expected”. It is not possible to store the value 2.7 in a double. Instead, the value stored in the machine register is something like 2.7000000000000001776… (continuing to 51 digits). You are lucky that the IEEE rounding rules allow the computation to give 27 at the end, but in general you should never expect a floating-point computation on inexact values to give an exact result.

    The value 2.5 can be represented exactly as a double, however. So you are guaranteed that 10 * 2.5 will be 25.

  • This heirarchy can cause some interesting issues. For example, you might expect the expression 5u - 10 to evalute to -5 (5u means 5 as an unsigned integer). But in this case, the signed integer (10) is promoted to an unsigned integer, and the result of this expression is the unsigned integer 4294967291!

    “Interesting” is one way to describe it….

  • Thanks for all these great examples and the helpful summary. Next time someone asks me why type safety matters I’m going to send them here.

  • Todd

    Typos.

    "Instead, it need (needs) to convert the integer 3"

    "Convering (Converting) between an integer and a floating point"

    Also, when discussing conversions between integers & floating points (you talk about converting in both directions), it may be more helpful to say, "Converting from ____ to ____ …" instead of "Converting between ____ and ____ …" so that the reader knows the direction of the conversion.

Leave a Comment

  

  

  

eighteen − nine =

Put C++ code inside [code][/code] tags to use the syntax highlighter