Comparing 3 Modern C++ Ways to Convert Integral Values to Strings

Comparing 3 modern c++ ways to convert integral values to strings

Question 1. Why is the string stream method consistently the worst?

The classical mistake: creating a new stringstream every single time

template<typename T> // 1. Using stringstream
string StringFromIntegral_SS(T const &value) {
thread_local stringstream ss;
ss.str("");
ss.clear();
ss << value;
return ss.str();
}

Question 2. Why is lexical cast consistently the best? Can we assume that this is the fastest implementation ?

Because it's most specialized; and, no, faster implementations exist. FastFormat and Boost Spirit have competitive offerings, as far as I know.

Update Boost Spirit Karma still easily beats the bunch:

template<typename T> // 4. Karma to string
std::string StringFromIntegral_K(T const &value) {
thread_local auto const gen = boost::spirit::traits::create_generator<T>::call();
thread_local char buf[20];
char* it = buf;
boost::spirit::karma::generate(it, gen, value);
return std::string(buf, it);
}

Timings:

C++ 11 method 111
String stream method 103
Lexical cast method 57
Spirit Karma method 36
Spirit Karma method with string_ref 13

See it Live On Coliru Clang or GCC


BONUS

Just to goof off, a version using boost::string_ref is much faster still due the reduced allocations:

template<typename T> // 5. Karma to string_ref
boost::string_ref StringFromIntegral_KSR(T const &value) {
thread_local auto const gen = boost::spirit::traits::create_generator<T>::call();
thread_local char buf[20];
char* it = buf;
boost::spirit::karma::generate(it, gen, value);
return boost::string_ref(buf, it-buf);
}

I've tested all modified methods for correctness using an asserting test loop:

return measure<>::execution(
//[&]() { for (auto const &i : v1) { func(i); }});
[&]() { for (auto const &i : v1) { assert(func(i) == StringFromIntegral_LC(i)); }});

Easiest way to convert int to string in C++

C++11 introduces std::stoi (and variants for each numeric type) and std::to_string, the counterparts of the C atoi and itoa but expressed in term of std::string.

#include <string> 

std::string s = std::to_string(42);

is therefore the shortest way I can think of. You can even omit naming the type, using the auto keyword:

auto s = std::to_string(42);

Note: see [string.conversions] (21.5 in n3242)

Compare two string as numeric value

Compare them digit by digit:

a = "3254353245423345432423133423421"
b = "3254353245423345432443133423421"

for(int i = 0; i < a.length(); ++i):
if ((a[i] - '0') < (b[i] - '0'))
{
std::cout << "b is larger!"
}

I'm sure you can take it from here if you want to find out whether b is larger than a, or if they are equal. Alternatively, if they are different lengths, the larger one wins! (Check for zeros at the beginning, i.e. "000443342") Don't forget to consider negative numbers.

C++: string.find finds a char in a string only if its on pos1

The return type of std::string::find() is the unsigned type std::string::size_type, and it returns either std::string::npos (which is the maximum value that std::string::size_type can represent) if the character was not found, or the first index of the found character in the string.

Now you are comparing the result of std::string::find() to the true, which results in integral promotion of the Boolean value true to the integral value 1. Thus, your condition is satisfied if and only if the character expected_char is found in position 1 (i.e. when it is the second character in the string).

If you want to check whether the character expected_char is in the string word, use

if (word.find(expected_char) != std::string::npos)
{
...
}

Verbatim stringify

Yes, add the manipulator(s) you desire to the function signature and forward them to the stream.

template<typename T, typename Manip> 
std::string Stringify(T const &value, Manip manip)
{
std::stringstream ss;
ss << manip << value;
return ss.str();
}

With the sample code;

int main()
{
using namespace std;
// The precision here is set to be sufficient to print the test platform
cout << Stringify(numeric_limits<float>::max(), setprecision(50)) << endl;
}

I assume that more than one manipulator will be used. To this end, function overloads can be added for the desired number of manipulators, or you can use (with C++11) variadic templates and perfect forwarding.

template <typename Stream>
Stream& AddManip(Stream& str)
{
// end the recursion
return str;
}

template <typename Stream, typename Head, typename... Tails>
Stream& AddManip(Stream& str, Head&& head, Tails&&... tails)
{
// add the head manipulator and forward the rest
str << std::forward<Head>(head);
return AddManip(str, std::forward<Tails>(tails)...);
}

template<typename T, typename... Manip>
std::string Stringify(T const &value, Manip&&... manip)
{
std::stringstream ss;
// add the manipulators to the stream
AddManip(ss, std::forward<Manip>(manip)...);
ss << value;
return ss.str();
}

int main()
{
using namespace std;
cout << Stringify(numeric_limits<int>::max(), setprecision(40), std::hex) << endl;
}

Is boost::lexical_cast redundant with c++11 stoi, stof and family?

boost::lexical_cast

  • handles more kinds of conversion, including iterator pairs, arrays, C strings, etc.
  • offers the same generic interface (sto* have different names for different types)
  • is locale-sensitive (sto*/to_string are only in part, e.g. lexical_cast can process thousands separators, while stoul usually doesn't)

Integer to string optimized function?

You may want to read this article by Alexei Alexandrescu, where he talks about low-level optimizations by using a (fixed-radix) int to string conversion as an example:

https://www.facebook.com/notes/facebook-engineering/three-optimization-tips-for-c/10151361643253920

Keep in mind that the most important thing when optimizing is always profiling.

How can I convert a std::string to int?

In C++11 there are some nice new convert functions from std::string to a number type.

So instead of

atoi( str.c_str() )

you can use

std::stoi( str )

where str is your number as std::string.

There are version for all flavours of numbers:
long stol(string), float stof(string), double stod(string),...
see http://en.cppreference.com/w/cpp/string/basic_string/stol



Related Topics



Leave a reply



Submit