Type Conversions

Type Conversions

As we have seen with the example of dividing two integer, operators are defined on specific types and return a specific type. What if we write 2./3? The first operand is a double, whereas the second is an integer. This is called a mixed expression. For consistency, one type must be converted to match the other before the operator is applied.

Implicit Conversions

Most compilers will automatically cast numeric variables to make mixed expressions consistent. The hierarchy, from lowest to highest, is bool -> char -> short int -> int -> unsigned int -> long -> unsigned -> long long -> float -> double -> long double.
Each variable will be promoted until all in the mixed expression are the same type.

Explicit conversion also occurs when a variable or literal of one type is assigned to a variable of another type. If the conversion is legal, the compiler will force the type of the quantity on the right-hand side of the assignment to match the declared type of the variable on the left-hand side.

The rules for numerical type conversions may result in some surprises. For example, when a float is converted to a double, the extra bits in the significand are filled (“padded”) with zeros. There is no magic that tells the compiler how to extend it “correctly.”
This can result in loss of precision or even seemingly-bizarre results, such as when a signed int is converted to an unsigned int.

To illustrate:

#include <iostream>
#include <iomanip>
#include <cmath>
using namespace std;

int main() {

   float r, x;
   double s;

   int i=-1100020;
   unsigned int j;
   j=i;
   cout<<"Signed to unsigned:"<<"\n"<<i<<"\n"<<j<<"\n";

   x=4.78;
   int k=x;
   cout<<setprecision(16)<<"Float to int:"<<"\n"<<x<<"\n"<<k<<"\n";

   s=acos(-1.0);
   r=s;

   cout<<setprecision(16)<<"Double to float:"<<"\n"<<s<<"\n"<<r<<"\n";

}

The result on a Unix system with g++ is

Signed to unsigned:
-1100020
4293867276
Float to int:
4.78000020980835
4
Double to float:
3.141592653589793
3.141592741012573

Notice that the compiler prints out 16 digits of the float variable when told to do so, even though they are incorrect past the 7th decimal place. Moreover, conversion to int from any floating-point type truncates, it does not round.

Explicit Conversion

Explicit type conversion is also called casting. Use explicit casting to be clear, or in circumstances such as argument lists where the compiler will not do it.

Explicit casting among numeric types:

r=(float) i;
j=(int) w;
d=(double) f;

The same phenomena apply as for implicit conversions when one numeric type is converted to another.

#include <iostream>
#include <iomanip>
using namespace std;

int main() {

   float r;
   double s;

   float o=1.;
   float t=3.;

   r=o/t;
   s=(double)r;

   cout<<setprecision(16)<<"Cast "<<s<<"\n";
   cout<<setprecision(16)<<"Literals "<<1./3.<<"\n";
}

The result on a Unix system with g++ is

Cast 0.3333333432674408
Literals 0.3333333333333333

Recall that literal floating-point values are typed as double by C++.

The above format was inherited from C. C++ also supports a functional notation:

r=float(i);
j=int(w);
d=double(f);

All these conversions are called C style casts. They are very permissive. This is not a problem for ordinary numeric variables as long as the programmer understands the rules. It can become a problem when converting from one pointer type to another.
We will discuss this in more detail when we talk about pointers.

Newer C++ style casts look like templates.

The static cast is most widely used and behaves similarly to the C-style casts.

int n=100000;
double s = static_cast<double>(n);

The dynamic_cast is mostly used for pointers or reference to classes and is beyond our scope here. The constant_cast can be used to both cast and alter the value of a variable declared const and likewise is beyond our scope in this introduction. Finally, a reinterpret_cast is defined; it is used to convert pointers from one type to another and also will not be discussed in this introduction.

String/Numeric Interconversions

C++ has introduced stringstreams for this purpose. Stringstreams are internal string buffers.

In this example we have included the line

using namespace std;

for convenience. We will discuss namespaces in when we talk about scope. In short, it makes the standard namespace the default, so that we may omit in before keywords such as cout and string.

#include <iostream>
#include <string>
#include <sstream>

using namespace std;

int main() {
    string age;
    int iage;
    iage=39

    //Convert numeric to character:
    stringstream ss;
    ss<<iage;       //load iage into buffer
    age=ss.str();
   //Convert character to numeric:
    age='51'
    stringstream ss2(age);
    ss2>>iage;
}

This may make more sense once we understand file input/output.

C++11 String Conversions

The C++11 standard has introduced several type-conversion functions:

Function Conversion
std::stoi string to integer
std::stol string to long
std::stoul string to unsigned integer
std::stoll string to long long
std::stof string to float
std::stod string to double
std::stold string to long double
std::to_string number to string
std::to_wstring number to wide string

These are in the standard string class for C++11 and above. Some compilers may require the addition of a -std=c++11 flag to access them.

Previous
Next