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:


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:


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:


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

This produces the expected result:


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:


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. To ensure this, the compiler uses the following rules:

  • If an operand is an integer that is narrower than an int, it undergoes integral promotion (as described above) to int or unsigned int.
  • If the operands still do not match, then the compiler finds the highest priority operand and implicitly 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 (included in the typeinfo header), 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

Note: Your compiler may display something slightly different as the format of is left up to the compiler.

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:


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)
4.3c -- Using statements

49 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”
    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


    "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.

  • Erik

    "Numeric promotion…


    Is the ‘f’ a typo, or what is its significance here?

  • Jennifer_3012

    Dear Alex,
    According to your website ,
    C++ has the following data types along with their format specifier:

        Int ("%d"): 32 Bit integer
        Long (%ld): 32 bit integer (same as Int for modern systems)
        Long Long ("%lld"): 64 bit integer
        Char ("%c"): Character type
        Float ("%f"): 32 bit real value
        Double ("%lf"): 64 bit real value

    Can you please look into this code

    if the sample input is 3 444 12345678912345 a 334.23 14049.30493

    then sample expected output should be

    The code mentioned produces only precision of 6 digits in double i.e.a6.
    Why ?

    Please help me out.
    Thank you. 🙂

  • I think you should write a bit more about typeid. What I discovered is that typeid can’t be used without header typeinfo. Include this in the tutorial if I m ri8.
    A question:

    I m using Code::Blocks. When I use short and int instead of long long and unsigned int in the above program, I got "39i" as result. I think Code::Blocks outputs character "i" to express an integer calculation (f for floating point). But when I compile and run the above code without replacing long long with short and unsigned int with int, it gives "39x". What is x? Please clear.

    Would be great if you can add some tutorials on template metaprogramming.
    Thnx Alex 🙂

    • Sorry, outputs are ‘i’ (not 39i) and ‘x’ (not 39x). ‘i’ for short l + int u and ‘x’ for long long l + unsigned int u. Please see my previous comment to understand everything. I m still confused what does ‘x’ means in case of long long and unsigned int calculation.

    • Alex

      Updated the lesson to include <typeinfo>.

      What typeid() returns is compiler specific. Visual Studio returns “nice” type names, like “int”. It looks like Code::Blocks returns mangled names. I presume Code::Blocks is using x to represent a 64-bit integer.

      • Jim

        FYI I’m using the newer version of Code::Blocks vs 16.01, on my Intel MB w/i7 chip, Windows 10-64bit.

        It output i 9 for integers when adding the two shorts and d 6 for double when adding the double and short in the two lesson programs above.
        So it is working okay, but names would have been nicer.  

        I don’t understand what your saying in your presumption- using x to represent 64-bit integer

        Code::Blocks users can upgrade to this version from Sourceforge website. Code::Blocks is not a compiler.

        • Alex

          The names displayed by are determined by the compiler. Visual Studio displays nice names (e.g. double), the compiler used by Code::Blocks displays short names (e.g. d).

          For 64-bit integers, Visual Studio shows __int64, whereas the compiler used by Code::Blocks shows “x” as the short name. It’s just a different way of indicating what type the variable is.

  • Jim


    So what your telling us, from your last example is, the compiler is not smart enough to convert a signed integer into and unsigned one, is that right?  Is this also true for signed and unsigned floats?

    Can you fool the compiler with this 5u - (10) or maybe this 5u - (10u)?

    • Alex

      No. The compiler can convert signed integers into unsigned integers. The problem here is that because the subtraction expression gets evaluated as the subtraction of two unsigned values, 5u - 10u evaluates to 4294967291 instead of -5 due to overflow.

      If you want this to work correctly, you’d need to use a static_cast to convert the unsigned number (5) to a signed number so the subtraction evaluated as two signed numbers.

      There’s no such thing as a signed or unsigned float. Floating point numbers are always signed.

  • Jim

    In this lesson you introduced typeid and .name. Can you please give us an explanation on how to use typeid and .name and the proper syntax?

    I would have thought they would have used typeof since they have sizeof as a keyword to make things easy to remember.

  • Abir hasan

    implicit conversion
    int x=5+3.55//5 is int and 3.55 float//5 converted in float but what is the type of int it int or float??

    • Alex

      x is defined as an int, so it stays an int.

      By the way, 3.55 is a double, not a float.

      So this statement is the equivalent of int x = 8.55, which causes 8.55 to be truncated to an integer and assigned to x.

  • Shiva


    In the beginning of the chapter, discussing the first case where implicit type conversion can happen, you wrote:

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

    Even though you are talking about assigning and initialising, both statements in the code are initialisations - implicit and explicit ones. This confused me, that I had to review the prior chapter about the topic to be sure. So, won’t this:

    be more appropriate to the description?

  • subh

    Hi Alex,

    For the below code using typeid(), I got different result than the mentioned one.
    mentioned output is double 6.0, but mine is d 6. Why there is a difference?

    #include <iostream>
    #include <typeinfo> // for typeid()

    int main()
        double d(4.0);
        short s(2);
        std::cout << typeid(d + s).name() << " " << d + s << std::endl; // show us the type of d + s

        return 0;

  • Anshul goel

    Love you Alex!

  • Matt

    In the second to last example, I was a little confused as to why the the two short integers got promoted to regular integer types during the arithmetic operation. To me, the wording right before the example (under "Evaluating arithmetic expressions"), made it seem as though an operand would only get promoted in this situation if the operands were of different types. As you wrote: "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."

    Maybe it should be re-worded to make it a little more clear for slow minds like mine 🙂

    • Alex

      I agree, the wording was confusing. I’ve updated the lesson text to better indicate that integers undergo promotion even if they match.

  • Dekasi

    "If an operand is an integer, it undergoes integral promotion (as described above)."
    This is a bit confusing because as I get it even if the both operands are int they undergo an integral promotion. If this is not correct you should write short (int) instead of integer (before the comma).

    • Alex

      Updated wording to: If an operand is an integer that is narrower than an int, it undergoes integral promotion (as described above) to int or unsigned int.

  • Miba

    Can you explain how signed int is converted to unsigned so it causes overflow?

    • Alex

      This program illustrates it:

      196 is 0x11000100 in binary. When we assign unsigned u to signed s, that 0x11000100 gets interpreted as a 2’s complement number instead of a normal binary number. 0x11000100 = -60 in 2’s complement.

      You’ll see that this program prints -60.

  • Nguyen

    Hi Alex,

    Please help!

    Example 1:

    int i = 3.5; // initialize an integer type with double value.
    std::cout << i;

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

    It really makes sense because I’ve learned that an integer type variable is a variable that can only hold non-fractional numbers (e.g. -2, -1, 0, 1, 2).


    Example 2:

    float i = 3; // initialize floating point type with integer value
    std::cout << i;

    The result is:

    I don’t understand why the result was not 3.0 because I’ve learned that a floating point type variable is a variable that can hold a real number, such as 4320.0, -3.33, or 0.01226. (it looks to me a real number is a number with a decimal point).

    Thanks, Have a great day.

    • Alex

      In example 2, your float variable i is holding the value 3.0, but std::cout prints 3 by default.

      If you want to print one decimal, you can do so as follows:

  • Nguyen

    Hi Alex,

    In the following example, we add two shorts:

    #include <iostream>
    #include <typeinfo> // for typeid()

    int main()
        short a(4);
        short b(5);
        std::cout << typeid(a + b).name() << " " << a + b << std::endl; // show us the type of a + b

        return 0;

    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.

    The size of short is 2 bytes (range: -32,768 to 32,767), this means 4 & 5 should be ok.  I don’t understand why they had to undergo integral promotion to ints before being added?


Leave a Comment

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