6.15 — 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 int value 3 into float variable f. Instead, it needs to convert the integer value 3 to a floating point number, which can then be assigned to float 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:

  • When assigning to or initializing a variable with a value of a different data type:

  • When passing a value to a function where the function parameter is of a different data type:

  • When 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 one data type to another data type.

There are two basic types of type conversion: implicit type conversion, where the compiler automatically transforms one data type into another, and explicit type conversion, 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 data type is expected, but a different data type is supplied. If the compiler can figure out how to do the conversion between the two types, it will. If it doesn’t know how, then it will fail with a compile error.

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 fundamental data type is converted into a value of a larger fundamental data type from the same family, 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, and signed short) to an int (if possible) or an unsigned int (otherwise).
  • 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 int and double are generally the most performant types to perform operations on.

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

For advanced readers

Under the hood, promotions generally involve extending the binary representation of a number (e.g. for integers, adding leading 0s).

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, code that causes an implicit conversion to be performed 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. 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 an unexpected 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 within the range of the floating type. For example:

This produces the expected result:


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

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


Conversions that could cause loss of information, e.g. floating point to integer, are called narrowing conversions. Since information loss is generally undesirable, brace initialization doesn’t allow narrowing conversions.

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. Since -5 can’t be stored in an unsigned int, the calculation wraps around, and we get an answer we don’t expect.

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

Quiz time

Question #1

What’s the difference between a numeric promotion and a numeric conversion?

Show Solution

6.16 -- Explicit type conversion (casting) and static_cast
6.14 -- The auto keyword

140 comments to 6.15 — Implicit type conversion (coercion)

  • jack

    Hi Alex, you wrote this example but in the comment I think you meant "return" instead of pass.

  • Kushagra

    Why overflow occurred when integer is promoted to unsigned integer?

    • Alex

      Because 5 - 10 only equals -5 with signed numbers. Unsigned numbers can't be negative, so the result overflows to a huge positive number.

  • dear alex,
    I created a program regarding to what you taught.
    here is the program.

    using namespace std;
    int main()
        unsigned long long a=1.2;
        int b=4;
    However the output was
    y        0.
    I would want to know what does the "y" stands for.
    thank you.

  • Cat

    Greetings, Alex! I'm sure I am not the only one with this confusion. I seem to not be understanding the precise difference between float and double in terms of variable assignment. in your example, is 6.0 called a double for the sake of the example (hypothetically if the programmer wanted more digit precision than float) or are there cases of two or three sig digs that would be considered a double?

    • Alex

      As you're aware, float and double are both floating point types, with double offering more precision than float, but using more memory.

      Variables are of whatever type they're defined as. Floating point literals are treated as floats if they have an f suffix, and as doubles if they have no suffix.

      6.0 is a double because d is a double, and s is a short, and when you add a double and a short, the result is a double. Precision doesn't really come into play at all here.

      I'm not sure if I actually answered your question.

  • gary

    Hello, alex, may i ask a question:


    is this a function to converting the data type? why do we need to use <int> instead of something like this static_cast(int,c)? thanks

    • Alex

      This converts the _value_ of object c into an integer (the type of object c itself remains unchanged).

      The <int> specifies the target type to convert to (in this case, an integer), and the object in parenthesis is the expression that gets evaluated to produce a single value to convert. In this case, that's just the variable c being evaluated for its value.

  • Good afternoon!!
    I have a question that I would like to ask:
    I heard about Project Euler and I would like to know if it is a great way to strengthen my skills in c++ or also what can I do every day in my life to use and strengthen my c++ skills?

    Thank you for the attention

  • 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?


  • 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:

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

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

  • 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 :)

  • Anshul goel

    Love you Alex!

  • 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;

  • 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?

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

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

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

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

  • 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. :)

  • Erik

    "Numeric promotion...


    Is the 'f' a typo, or what is its significance 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.

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

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

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

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

  • x = static_cast(stuff);

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

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

  • 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)

  • 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

  • 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. :)

Leave a Comment

Put all code inside code tags: [code]your code here[/code]