CS 70

Promotion and Conversion

  • Dog speaking

    If you add a short int to a long int do you get a regular int?

  • LHS Cow speaking

    …No. But we do need to talk about these kinds of questions!

When Everything is the Same Type

Everything is easiest when we're working with the same type, so…

  • An int added to an int makes an int
  • A long double added to a long double makes a long double.

So if we say

long usPopulation    = 336858333;
long usAreaInSqMiles = 3718712;
std::cout << "People per square mile = "
          << usPopulation/usAreaInSqMiles
          << std::endl;

the calculation usPopulation/usAreaInSqMiles takes in two longs and the result is a long. So it will print

People per square mile = 90
  • Horse speaking

    Hey! That's not right! 336858333/3718712 is 90.584679050165761!

  • Duck speaking

    And it should at least round it properly to 91 if it's going to make it be a long!

  • Hedgehog speaking

    Gah!

  • LHS Cow speaking

    Okay, so this is a good point and it's why we're covering this topic. It's one of the most common errors people make when they're first learning C++.

  • RHS Cow speaking

    We're following exactly the rule we gave earlier, usPopulation and usAreaInSqMiles are both longs, so C++ uses long's division operator, which does integer division, and truncates the result to an integer rather than rounding it.

If, however, we'd instead said

double usPopulation    = 336858333.0;
double usAreaInSqMiles = 3718712.0;
std::cout << "People per square mile = "
          << usPopulation/usAreaInSqMiles
          << std::endl;

it would have used double's division operator and produced the output:

People per square mile = 90.5847
  • Cat speaking

    Dare I ask why we have to put .0 after the numbers in the constants?

  • LHS Cow speaking

    To make it clear that the constant is a doubles, rathwer ints.

  • Pig speaking

    And why doesn't the result have MORE digits after the decimal point? It doesn't seem very precise for a double!

  • LHS Cow speaking

    That's just the way it prints numbers by default, there are more digits, they're just not shown when printing. There's a way to change it, but we'll get to that later.

Promotion: Different Sizes of the Same Type

Let's assume that long is a bigger type than int (specifically, that we're using the LP64 memory model where int is 32 bits and long is 64 bits.).

If we do something like:

long usPopulation   = 336858333;
int thisWeeksGain   = 36538;
std::cout << "US population is now "
          << usPopulation + thisWeeksGain
          << std::endl;

C++ does the sensible thing, it takes the int holding thisWeeksGain and widens it to be a long before it adds it to usPopulation, so the final result we're printing is a long. This widening process is called a promotion.

It would also be considered a promotion if we had declared thisWeeksGain as an unsigned int, so long as it's smaller than a long. This is because the range of an unsigned int is [0…4,294,967,297] and the range of a long is [−9,223,372,036,854,775,808…9,223,372,036,854,775,807], so the range of an unsigned int falls entirely inside the range of a long.

C++ performs promotions automatically, and its rule for promotion is

  • The types must be the same kind (integer to integer or floating point to floating point).
  • The range of the type being promoted to must completely include the range of the type being promoted from.

Thus, a promotion cannot ever cause information to be lost.

Any other kind of conversion between numeric types is a conversion.

We can also get C++ to perform promotions when we initialize new variables.

short weekDay = 3;
int yearStart = weekDay;

Explicit Promotions

We can also perform promotions explicitly. For example,

signed char dollar = 36;
std::cout << "Printed as a character " << dollar << std::endl
          << "Printed as an integer  " << int(dollar) << std::endl;

prints

Printed as a character $
Printed as an integer  36
  • RHS Cow speaking

    We use the exact same syntax for conversions as well. int(3.1415) is 3.

  • LHS Cow speaking

    And conversions are our next topic!

Deeper Dive: Auto-Promotion of Small Integers

Integer types smaller than an int (e.g., short int and signed char) always promote to an int when we use them in arithmetic. Somewhat strangely, small unsigned ints promote to int not unsigned int.

Conversion: Changing Between Types

If we move a numeric value between types and it isn't a promotion, it's a conversion.

Suppose instead we had written

double usPopulation = 336858333;
int thisWeeksGain   = 36538;
std::cout << "US population is now "
          << usPopulation + thisWeeksGain
          << std::endl;

Here C++ would have to make a choice for what type the result of usPopulation + thisWeeksGain should be. Given a choice between an int and a double for the result of the addition, it converts the type to a double.

  • RHS Cow speaking

    Notice that C++ didn't ask us if it's okay to perform the conversion.

  • Dog speaking

    It'd be kinda weird if it stopped and asked us questions while compiling our code!

  • LHS Cow speaking

    True.

  • Duck speaking

    But why can't it compile the code without doing a conversion?

  • LHS Cow speaking

    The only kind of arithmetic operators that exist for numbers are ones where both the lefthand side and righthand side are the same type, so it needed to do addition with two ints or with two doubles.

Conversions can potentially lose information!

If we run this code

long worldPop = 7960583250;
std::cout << "World pop as long " << worldPop << std::endl
          << "----------> float " << float(worldPop) << std::endl
          << "-----------> long " << long(float(worldPop)) << std::endl;

it prints

World pop as long 7960583250
----------> float 7.96058e+09
-----------> long 7960583168
  • Hedgehog speaking

    Oh dear, we lost 82 people!

Some rules:

  • Conversions (and promotions) often happen automatically, particularly when we call a function that wants a different type from the one we're providing but we can convert our value to that type.
  • Changing any kind of integer to any kind of float (or the other way) is always considered a conversion (even if on a particular machine no information could be lost for those particular sizes, so chardouble is still a conversion).
  • Changing to a smaller incarnation of the basic type is always a conversion (e.g., intshort int or doublefloat).
  • Signed→unsigned is always a conversion (e.g., short intunsigned long int is a conversion).
  • Unsigned→signed is usually a conversion (unless the signed type is larger, which it usually isn't).
  • Duck speaking

    What happens if I do a - b and a is a signed int and b is an unsigned int?

  • LHS Cow speaking

    Good question! The result is an unsigned int. This can be very surprising if you were expecting to get a negative number!

Conversions and Promotions Aren't Done “Early”

While conversions and promotions happen automatically, they happen no earlier than needed. So if we say:

long usPopulation    = 336858333;
long usAreaInSqMiles = 3718712;
double popPerSqMile  = usPopulation/usAreaInSqMiles;

It will first perform division on longs to produce a long result and only afterwards will it convert it to a double. To get a double as the result from division, we need to be dividing doubles.

  • Cat speaking

    That might easily catch me out, I think.

  • LHS Cow speaking

    It catches everyone out at least some of the time.

  • Bjarne speaking

    Sorry.

  • LHS Cow speaking

    That's okay, Bjarne Stroustrup, creator of C++. The other way would have caused problems too!

  • RHS Cow speaking

    coughcoughthisiswhyPythonhastwodivisionoperatorscoughcough

So we need to write:

long usPopulation    = 336858333;
long usAreaInSqMiles = 3718712;
double popPerSqMile  = double(usPopulation) / double(usAreaInSqMiles);
  • LHS Cow speaking

    Actually, you only need one of those double(…) conversion operators, because once we have a double on one side, it'll auto convert the other side to a double.

Suppose that x is an int. The C++ expression y = x would be an example of promotion if y had which of the following types?

Summary: Promotion and Conversion

  • RHS Cow speaking

    Here's a summary table with examples of the different types of conversion and promotion we've seen!

Category Example(s) Promotion or Conversion? Why?
Smaller to bigger type of the same kind int to long int Promotion long ints use at least as many bits as ints.
Bigger to smaller type of the same kind int to char Conversion long ints use at least as many bits as ints.
Signed/unsigned integer types int to unsigned int
unsigned int to int Conversion the ranges of signed and unsigned types are different
Different kinds of numbers (int vs. float) int to float
float to int Conversion integers and floating point numbers are different

(When logged in, completion status appears here.)