Search

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 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. 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, any code that does an implicit conversion will often cause the compiler to issue a warning (on the other hand, if you do an explicit conversion using a cast, the compiler will assume you know what you’re doing and not issue a warning). Under the hood, conversions typically require converting the underlying binary representation to a different format.

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 an undefined 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 within the range of the floating type. For example:

This produces the expected result:

10

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:

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. 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 typeid.name() 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:

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.

Quiz time!

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

Show Solution

4.4a -- Explicit type conversion (casting)
Index
4.3c -- Using statements

94 comments to 4.4 — Implicit type conversion (coercion)

  • I'm in love with alex's tutor

    Peace for you Mr. Alex!
    Can you nominate us a book or website that explains in which header file(library) a built in and pre-compiled functions are found and what they do? like pow(), std::setprecision(),fail() and a like....
    B/s knowing them is a best way to shorten our code and to know more about C++ functionality. Can i get them in Visual studio help desk?
    God be always with you  !!!

    • https://en.cppreference.com/w/cpp
      http://www.cplusplus.com/reference/

      Use the search function on either one of those sites.
      cpluscplus doesn't use https and is slower, but has better examples than cppreference in my opinion.

      • I'm in love with Alex's tutorial

        Thank you so much Mr Nas!!!
        I always proud of your helpful and prompt responses.
        I really feel blessed to have you both!
        Thanks much indeed!

  • Yaroslav

    Hello.
    Here is a text.
    "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."
    what does "to an integer" means? They are all integers. Does it a typo of int as another word for signed int or what? I think it's a bit unclear.

  • Max Ludero

    Hello. Why do you need to #include <typeinfo> for typeid when typeid is a C++ keyword? Thanks!

    • Alex

      Because typeid returns an object of type std::type_info, which is defined in the typeinfo header.

      It's a bit of a strange design.

      • Max Ludero

        Thank you very much, Alex.
        I was a little confused, because when I originally tried it, it worked without #include <typeinfo>. Then I realized that it was being included with <iostream>. Same with some of the other headers, like <cstdint>. I understand that they should all be #included anyway as a matter of good programming practice. I am using XCode.

  • Vikram

    this is not  ambiguous, conversion does not work on pointers and addresses, except void and 0(zero)

  • Vikram

    this is also ambiguous.
    int can be demoted to char and promoted to double also.
    This int demotion to char is not said in this article.
    Please put some lines for demotion from int to char or some other smaller data types

    • Alex

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

      The numeric conversions section has an example of an int (typically 32 bits) being converted to a short (typically 16 bits).

  • Infinite

    In the last example, why does it overflow instead of becoming 15?

    • nascardriver

      Hi Infinite!

      Are you talking about this?

      Why would it be 15? It's subtraction, not addition.

      • Infinite

        But the - 10 was changed to unsigned, so shouldn't it become 10?

        • nascardriver

          That's not how unsigned works. If an unsigned number goes below 0 it will start over at the maximum value.

          This is the same as Alex' code, but it's clearer that 10 is positive and unsigned. The result is unsigned too, but an unsigned int can't hold negative numbers.

          Let's do the calculation in binary, because that's what your computer does and that's what causing the result. I'm using 8bit integers, because they're shorter:

          With regular ints, the first bit indicates whether or not the number is negative. 1 means negative. You can convert a negative number to decimal by flipping all bits and adding 1, that will give you the positive number.

          Now, the subtraction in unsigned will yield the same 1111 1011, but unsigned integers don't use the first bit as a sign bit, the use it as part of the number.
          Using the first bit as part of the number gives us 1111 1011 = 251 in decimal.
          32 bits looks the same, just with more leading ones and zeroes.
          You don't need to understand how this works on a binary level, but you need to know that when an unsigned number tries going below 0 it will start over at the highest number.

        • DonnieDarko

          Binary operator and integer sign is different.
          Here: 5u - 10 doesn't mean x = 5u and y = -10;
          You can see it as 5u - (10), here 10 is of singed integer and [if it would have been variable its range would have been from (-2147483648 to 2147483647)] and '-' is the binary operator between 5u and 10.
          10 will convert to unsigned integer implicitly [getting the range from (0 to 4294967295) if it would have been variable].

  • hrmn

    These issue warning? , as we studied earlier short s=2; works fine . should i use it in my code?

    or these issue warning after crossing range.I was thinking to use it in for variables with short range constraints. By default numbers 1,2,3.. represent int integer?
    Thanks.
    These tuts are great!

    • nascardriver

      Hi hrmn!

      Neither g++ nor clang++ issue a conversion warning for your code. You should only get a warning when the values are out-of-range.

      > By default numbers 1,2,3.. represent int integer?
      Correct, that's why you shouldn't use them to initialize a double. "short" is short for "short int" so it's fine there.

      > should i use it in my code?
      Use uniform initialization and numbers that match your type.

      • hrmn

        ""short" is short for "short int" so it's fine there."- i didn't get what it means.

        int integer(2 or 4 byte) is converted to short int(2 byte) (might be loss),
        in case of double int integer is promoted (and no loss)
        whats the difference , why shouldn't we use them to initialize the double.

        Thanks for reply.

        • nascardriver

          > i didn't get what it means.
          Whether you write

          doesn't matter, "short" is just there so you don't have to write "short int" all the time. A "short" is an "int", so you can initialize it with integers.

          > why shouldn't we use them to initialize the double.
          When your teacher tells you do bring a checkered piece of paper, do you bring one with lines and draw on the vertical ones? It's the same in the end, but one way is better than the other. Not the best of analogies.
          You'll learn about the auto-keyword later, it deduces the type from the value you give it. When you pass an int it will be an int, if you want a double you need to give it a double. If you decide to initialize doubles with int you'll end up with mixed notations.

          • hrmn

            default number like 1,2,3 are  which type of int ?
            short int , int ,long,long long.

            sorry i am confused ,  thinking short integer and int integer separate

            • nascardriver

              "short int", "int", "long int" and so on are different, but they're all integers. You can initialize all of them with integer numbers(1, 2, 3, ...) and that's the proper way to initialize them. The only thing you need to care about with integers are their range.

  • Cosmin

    What's happening wrong in this program?

    I understand why a * 2 evaluates to int, but why does unsigned long long b = a * 2 work correctly?

    • nascardriver

      Hi Cosmin!

      The uniform initializer will cause your compiler to to print a warning, because a * 2 is not an unsigned long long.

  • Frederico Machado

    I think the first example in numeric conversion is actually doing a promotion.

    • Alex

      Nope. Numeric promotion is converting a smaller integral (or floating point) type to a larger integral (or floating point) type.

      The first example in numeric conversion is converting an integral number to a floating point value, which is a conversion, not a promotion.

      Promotions generally involve extending the binary representation of a number (e.g. for integers, adding leading 0s), whereas conversions require converting the underlying binary representation to a different format.

      • WiseFool

        Hi, Alex
        That explains it very succinctly and clearly.  I have a little suggestion that maybe you should make it a little quiz question at the end of this section (4.4) of "Explain the difference between numeric promotion and numeric conversion." with your above explanation as the answer.  That way it won't be lost as older comments are cleared. 😉

Leave a Comment

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