Search

8.3 — Numeric conversions

In the previous lesson (8.2 -- Floating-point and integral promotion), we covered numeric promotions, which are conversions of specific narrower numeric types to wider numeric types (typically int or double) that can be processed efficiently.

C++ supports another category of numeric type conversions, called numeric conversions, that cover additional type conversions not covered by the numeric promotion rules.

Key insight

Any type conversion covered by the numeric promotion rules (8.2 -- Floating-point and integral promotion) is a numeric promotion, not a numeric conversion.

There are five basic types of numeric conversions.

1) Converting an integral type to any other integral type (excluding integral promotions):

2) Converting a floating point type to any other floating point type (excluding floating point promotions):

3) Converting a floating point type to any integral type:

4) Converting an integral type to any floating point type:

5) Converting an integral type or a floating point type to a bool:

As an aside...

Because brace initialization disallows some numeric conversions (more on this in a moment), we use copy initialization in this lesson (which does not have any such limitations) in order to keep the examples simple.

Narrowing conversions

Unlike a numeric promotion (which is always safe), a numeric conversion may (or may not) result in the loss of data or precision.

Some numeric conversions are always safe (such as int to long, or int to double). Other numeric conversions, such as double to int, may result in the loss of data (depending on the specific value being converted and/or the range of the underlying types):

In C++, a narrowing conversion is a numeric conversion that may result in the loss of data. Such narrowing conversions include:

  • From a floating point type to an integral type.
  • From a wider floating point type to a narrower floating point type, unless the value being converted is constexpr and is in range of the destination type (even if it can not be represented exactly).
  • From an integer to a floating point type, unless the value being converted is constexpr and is in range of the destination type and can be converted back into the original type without data loss.
  • From a wider integer type to a narrower integer type, unless the value being converted is constexpr and after integral promotion will fit into the destination type.

The good news is that you don’t need to remember these. Your compiler will usually issue a warning (or error) when it determines that an implicit narrowing conversion is required.

Warning

Compilers will often not warn when converting a signed int to an unsigned int, or vice-versa, even though these are narrowing conversions. Be extra careful of inadvertent conversions between these types (particularly when passing an argument to a function taking a parameter of the opposite sign).

For example, when compiling the following program:

Visual Studio produces the following warning:

warning C4244: 'initializing': conversion from 'double' to 'int', possible loss of data

In general, narrowing conversions should be avoided, but there are situational cases where you might need to do one. In such cases, you should make the implicit narrowing conversion explicit by using static_cast. For example:

Best practice

Avoid narrowing conversions whenever possible. If you do need to perform one, use static_cast to make it an explicit conversion.

Brace initialization disallows narrowing conversions

Narrowing conversions are strictly disallowed when using brace initialization (which is one of the primary reasons this initialization form is preferred):

Visual Studio produces the following error:

error C2397: conversion from 'double' to 'int' requires a narrowing conversion

More on numeric conversions

The specific rules for numeric conversions are complicated and numerous, so here are the most important things to remember.

In all cases, converting a value into a type whose range doesn’t support that value will lead to results that are probably unexpected. For example:

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

48

Converting from a larger integral or floating point type to a smaller type from the same family 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 point 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

While the numeric conversion rules might seem scary, in reality the compiler will generally warn you if you try to do something dangerous (excluding some signed/unsigned conversions).


8.4 -- Arithmetic conversions
Index
8.2 -- Floating-point and integral promotion

11 comments to 8.3 — Numeric conversions

  • Rostyslav

    From the list of narrowing convertions:
    3."From an integer to a floating point type, unless the value being converted is constexpr and is in range of the destination type and can be converted back into the original type without data loss."
    Isn't the last part always true if the integer is in the range of the destination type? I.e. how can data be lost when converting float to int given both are wide enough for the given value?
    4."From a wider integer type to a narrower integer type, unless the value being converted is constexpr and after integral promotion will fit into the destination type." Should not it be "convertion" instead of "promotion"?

  • HangUp

    Hello, I think I found a small typo in "Because brace initialization disallows some numeric conversions (more on this is a moment)..." where "more on this is a moment" should be "more on this in a moment".

    • HangUp

      as well as in the last line where "While the numeric conversion rules might seen scary" should be "While the numeric conversion rules might seem scary".

  • Mateusz Kacperski

    You need to include header <iomanip> for std::setprecision if someone have a error:
    "‘setprecision’ is not a member of ‘std’ "

  • Mateusz Kacperski

    i found error:

    "Converting from a larger integral or floating point type to a smaller type from the same family will generally work so long as the value fits in the range of the smaller type. For example:"

    Should be:

    Converting from a larger ( INTEGER [not integral] ) or floating point type to a smaller type from the same family will generally work so long as the value fits in the range of the smaller type. For example:

    • Jake

      I think it's the right word
      integer refers to numbers that don't have a fractional part

      while integral  refers
      to Booleans, integer, characters etc as a whole

  • Mateusz Kacperski

    "Because brace initialization disallows some numeric conversions..."

    How i can know and when i can know i should use 'brace initialization' or '=' ?

    • Alex

      You should always use brace initialization unless you have a specific reason not to. If you are intentionally doing an initialization that is a narrowing conversion, you can either use brace initialization and static_cast your initializer to the narrower type, or use direct or copy initialization instead.

  • Aasif Ali

    "Brace initialization", this word has been used many times on this site. can anyone tell me what does it actually mean?
    I searched it on google but could not understand the definition

    • Mateusz Kacperski

      "Brace initialization":

      int a{ 3 };

      "copy initialization":

      int a = 3;
      But don't ask me when we should use which :P:P i have no idea xD

  • Aah, these last couple of lessons, especially braces not allowing type conversion and the use or static_cast has answered some head scratching I was seeing doing the end of Chpt10 Blackjack project.

Leave a Comment

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