3.2. Converting from double to int

As I mentioned, C++ converts int to double automatically if necessary, because no information is lost in the translation. This is called a widening conversion.

On the other hand, going from a double to an int truncates the result. When a floating point value is truncated, all of the information after the decimal place is lost – as if it never existed. The number is not rounded up, even if the fractional part is .9. C++ (usually) warns, but does not prevent this loss of information. This loss is called a narrowing conversion. Does this mean that once you have an double, it can never be assigned to a int?

Of course not.

You can explicitly perform these kinds of assignments by using a typecast.

The simplest way to convert a floating-point value to an integer is to use a typecast. Typecasting is so called because it allows you to take a value that belongs to one type and “cast” it into another type (in the sense of molding or reforming, not throwing).

There are 2 general forms for a cast in C++.

The first syntax for typecasting is like the syntax for a function call. For example:

constexpr double pi = 3.14159;
int x = int (pi);

The int function returns an integer, so x gets the value 3. Converting to an integer always truncates, even if the fraction part is 0.99999999.

The second syntax, called a named cast is a bit more verbose. For example:

constexpr double pi = 3.14159;
int x = static_cast<int> (pi);

Other than the syntax that appears more like a vector than a function call, the behavior is identical to the functional form. One advantage of the named cast is that the entire type name is a parameter, so:

long long x = static_cast<long long> (pi);   // this works
long long x = long long (pi);                // this does not: error!

For every built-in type in C++, there is a corresponding function that casts its argument to the appropriate type.

Caution

With great power comes great responibility.

Casts are commonly misused. Use them with caution, or even better – not at all.

C++ also inherits from C an older C-style cast syntax. Unfortunately, this type of cast is even more willing to bypass narrowing conversions. The C-style syntax places the () around the type rather than the value:

constexpr double pi = 3.14159;
int x = (int) pi;

Casts like this can result in unexpected or implementation defined behavior. For example:

What does this program produce?

Unfortunately, a simple internet search will commonly find C++ code that still uses the old C-style cast syntax.

Although the language allows us to write this kind of code, you almost never should.

As a general rule, you should avoid casting if possible. Often a cast is performed when initializing a new variable from an old one. This is exactly the place where errors can creep in.

If you know that you do not want to allow any narrowing conversions, then there is a simple remedy, starting with C++11: Use explicit type construction instead of a cast.

constexpr double pi = 3.14159;
int x = int (pi);     // compiles. Narrows pi to 3
int y = int {pi};     // Error. narrowing conversion from 'double' to 'int'

The explicit type construction will never implicitly allow a type conversion that results in data loss.

Run the examples on each tab to see the results. In each case, the code is (mostly) the same, but the results may be different.

Try changing char p to auto p and see what happens.

Try changing char p to auto p and see what happens.

Try changing char p to auto p and see what happens.

Try changing char p to auto p and see what happens.

To summarize, casting to some type T ordered from worst to best:

C-style cast

new_value = (T) value;

Never protects against inappropriate conversions. Avoid.

Functional cast

new_value = T (value);

Protects against inappropriate conversions. Allows narrowing conversions.

Can’t be used on types with spaces in the type name, such as long long and unsigned int.

Named cast

new_value = static_cast<T> (value);

Protects against inappropriate conversions. Allows narrowing conversions.

Allows converting to types with spaces in the type name, such as long long and unsigned int.

Explicit type construction

new_value = T {value};

The T{value} construction syntax makes it explicit that construction is desired. The T{value} construction syntax doesn’t allow narrowing. It is the only safe and general expression for constructing a value of type T from a value or an expression.


More to Explore

You have attempted of activities on this page