Sunday, May 13, 2012

String to int / double Conversions

Before the days of C++11, conversions from numeric types to their string representation would typically be done as follows:


template <typename T>
inline std::string to_string(T v)
{
 std::ostringstream ss;
 ss << v;
 return ss.str();
}


std::string s1 = to_string(1024);
std::string s2 = to_string(3.14);


Now, C++11 provides the function directly:

std::string s1 = std::to_string(1024); // use std::to_string(int) 
auto s2 = std::to_string(3.14); // use std::to_string(double)

Unfortunately, MSVC 10's STL does not provide overloads for int or double; but only for long long, unsigned long long, and long double (essentially, the largest signed integer, unsigned integer, and floating type).

So we need to use the appropriate type or cast first:


// Fix for MSVC 10 bug:
std::string s1 = std::to_string(1024LL); // use std::to_string(long long)
auto s2 = std::to_string(3.14L); // use std::to_string(long double)


int myval;
// ... 
auto s3 = std::to_string(long long(myval)); // cast to long long first

This solves the problem of converting string to int, or string to double.

More interestingly, C++11 also provides int to string and double to string conversions.
Before C++11, the obvious solution would be:


template <typename T>
inline T destringify(const std::string& s)
{
 std::istringstream ss(s);
 T v;
 ss >> v;
 return v;
}

Then we "destringify" like this:


int i = destringify<int>("1024");
double d = destringify<double>("3.14");

This has some problems, when the string is not of the expected format:


int i  = destringify<int>("three"); // what will i be?
double d = destringify<double>("-x"); // what will d be?
double d2 = destringify<double>("3.14x"); // ignores the 'x': d2 will be 3.14

C++11 provides std::stoi and std::stod for string to int and double conversion, respectively.


int i = std::stoi("1024"); // as expected: i is 1024
double d = std::stod("3.14"); // as expected: d is 3.14

But what happens when we have an invalid format?
The first two cases above, that is, using "three" and "-x", will be reported as errors. However, the last case, "3.14x", will do what our destringify already does: ignore the 'x' and give the value 3.14:

try {
 int i = std::stoi("three"); // will throw std::invalid_argument
} catch (std::invalid_argument) {
 std::cerr << "Error: invalid argument \"three\"\n"
}

And same for "-x":


try {
 int i = std::stod("-x"); // will throw std::invalid_argument
} catch (std::invalid_argument) {
 std::cerr << "Error: invalid argument \"-x\"\n";
}

But the last conversion might give you a silent surprise:


try {
 int i = std::stod("3.14x"); // i is 3.14, 'x' was simply ignored
 int j = std::stod("3.14x567"); // j is 3.14 (discards everything after 'x' too)
} catch (std::invalid_argument) {
        // we never end up here
}