Literals and Type Conversion

Narrowing Conversions

Narrowing conversions are implicit conversions that may result in losing information. The compiler will allow them automatically, but in many cases the new type isn't wide enough or precise enough to hold every possible value of the previous type.

An extra bit may be thrown away, the fractional part may be thrown away, or the number may wrap around. The program then proceeds using the converted value.

All these conversions are permitted by ordinary assignment as well as in many contexts of expression. They are often tolerated by the language without requiring a diagnostic, and so the loss of data can go unnoticed until a bug produces an incorrect result.

A conversion is narrowing if the target type is unable to represent all the values of the source type.

Typical situations include:

  • A floating point type to an integer type conversion

  • A larger integer type to a smaller integer type conversion

  • An unsigned type becoming a signed type when the value exceeds the range of the signed type

  • A signed type becoming an unsigned type when the value is negative

The conversion is still taking place. The compiler doesn't necessarily refuse the code. It results in another value.

When converting a floating point value to an int, the fractional part of the number is lost. The conversion truncates toward zero.

double price{19.99};
int dollars = price;

The source floating point variable stays the same. Conversion yields an integer value that just contains the integer part, and this value is stored in the destination variable.

The same applies to negative numbers. A value such as -19.99 becomes -19. No rounding is performed; the fractional part is chopped off.

Integer types might have different ranges. Shortening a value by moving it from a wider, more capable integer type to a narrower, less capable one may give a different value than the original if the destination type will not be able to hold the value.

int big{40000};
short small = big;

The same sort of loss will happen when assigning an int to a char, or a long long to an int of narrower range.

The compiler might silently convert it in a normal assignment.

Converting a negative value to an unsigned type results in a positive value according to modulo arithmetic.

int negative{-5};
unsigned int wrapped = negative;

An implementation defined result occurs when converting a large unsigned value to a signed type that is incapable of representing it.

Sign information may be lost in these conversions. While the behavior follows the language rules, the value can become very different from what the reader will be expecting.

Narrowing can take place in regular assignment, as a function argument, and as the operand of an expression.

The value is converted before it is stored or before the operation is performed. The rest of the expression therefore operates on the converted value.

As no diagnostic is always needed, narrowing may go unnoticed unless the source and destination types are checked closely.

When converted from a floating point value to an int, the fractional part is truncated.

When converting between integers of different sizes, a larger integer converted to a smaller integer can result in a different value if the larger integer's value cannot be represented in the smaller integer type.

When a negative signed integer is assigned to an unsigned type, the value will be converted into a positive value according to modular arithmetic.

A large value initially unsigned, then converted to a signed type, may yield an implementation defined result.

An integer converted to a char may also discard information if the value is not within the range representable by char.

#include <iostream>

int main()
{
    double almost{7.89};
    int whole = almost;

    std::cout << "7.89 narrowed to int: "
              << whole
              << '\n';

    int wide{40000};
    short narrow = wide;

    std::cout << "40000 narrowed to short: "
              << narrow
              << '\n';

    int neg{-10};
    unsigned int pos = neg;

    std::cout << "-10 converted to unsigned: "
              << pos
              << '\n';

    unsigned int big_unsigned{4000000000u};
    int signed_result = big_unsigned;

    std::cout << "4000000000u converted to int: "
              << signed_result
              << '\n';

    int code{300};
    char letter = code;

    std::cout << "300 narrowed to char as a numeric value: "
              // Perform this conversion explicitly
              << static_cast<int>(letter)
              << '\n';
}
Exercise: Narrowing ConversionsStarter code, hints and solutionOpenClose
Guided practiceBeginner · 10–15 min

Practice: Narrowing Conversions

Work through the mission, reveal hints only when needed, and compare your result with the model solution.

Your challenge

Change almost to 4.75 and update its label. Predict whole, then distinguish fractional truncation from the other examples that may exceed a destination type range.

Mission checklist

  1. Run the starter as a baseline, then predict the change requested in Your challenge.
  2. Make that change in your editor; use the lesson-specific hints if you need help.
  3. Compile and run your answer, then compare with the worked solution and explain the result.

Starter program

This is the working baseline, not the completed answer. Compile as C++20, then make the change described above.

#include <iostream>

int main()
{
    double almost{7.89};
    int whole = almost;

    std::cout << "7.89 narrowed to int: "
              << whole
              << '\n';

    int wide{40000};
    short narrow = wide;

    std::cout << "40000 narrowed to short: "
              << narrow
              << '\n';

    int neg{-10};
    unsigned int pos = neg;

    std::cout << "-10 converted to unsigned: "
              << pos
              << '\n';

    unsigned int big_unsigned{4000000000u};
    int signed_result = big_unsigned;

    std::cout << "4000000000u converted to int: "
              << signed_result
              << '\n';

    int code{300};
    char letter = code;

    std::cout << "300 narrowed to char as a numeric value: "
              // Perform this conversion explicitly
              << static_cast<int>(letter)
              << '\n';
}

Hints

Hint 1

Focus first on int whole = almost: converting 7.89 to int removes its fraction, not rounds it to the nearest integer. Try another small in-range double.

Hint 2

The short and char examples depend on the destination range; do not assume a portable numeric result. Compare copy initialization with int whole{almost}, which rejects this narrowing conversion.

Worked solution

almost is changed to 4.75; conversion to int gives 4, not 5. The other narrowing examples remain to illustrate range loss. Their numeric results can depend on type widths and implementation rules, so the output shown is one tested run.

#include <iostream>

int main()
{
    double almost{4.75};
    int whole = almost;

    std::cout << "4.75 narrowed to int: "
              << whole
              << '\n';

    int wide{40000};
    short narrow = wide;

    std::cout << "40000 narrowed to short: "
              << narrow
              << '\n';

    int neg{-10};
    unsigned int pos = neg;

    std::cout << "-10 converted to unsigned: "
              << pos
              << '\n';

    unsigned int big_unsigned{4000000000u};
    int signed_result = big_unsigned;

    std::cout << "4000000000u converted to int: "
              << signed_result
              << '\n';

    int code{300};
    char letter = code;

    std::cout << "300 narrowed to char as a numeric value: "
              // Perform this conversion explicitly
              << static_cast<int>(letter)
              << '\n';
}

Example output

This is one tested C++20 run. Sizes, capacity, formatting, floating-point details, or extra container lifetime messages may differ by implementation; use the explanation to identify the guaranteed result.

4.75 narrowed to int: 4
40000 narrowed to short: -25536
-10 converted to unsigned: 4294967286
4000000000u converted to int: -294967296
300 narrowed to char as a numeric value: 44

0 comments

No comments yet. Be the first.

Add a comment

About CppLearn

CppLearn is an interactive, free reference for modern C++. In each lesson, you'll find out how the language works, rather than just lines of code that you can copy and paste. We cover concepts starting from beginner lessons on fundamentals and proceed to templates, move semantics, smart pointers, and areas that usually trap or scare C++ learners.