2012-04-27

Easy but not too slow integer to string conversion

In my previous post I compared the speed of different methods of converting integer to string. The problem was that these solutions both relied on the environment somehow and cannot be used on an adhoc manner like messages[i] = std::string("Error found at line #") + IntToString(lineNumbers[i]) + ".";. These both need some circumstancial preparation like creating a buffer (char buffer[...]) or a std::stringstream outside the loop and resetting it after usage. So I started on a journey to create a kitchen-ready preparation-less yet not too slow implementation for my imaginary IntToString() function.

#include <iostream>
#include <limits>
#include <sstream>
#define __STDC_FORMAT_MACROS
#include <inttypes.h>

// Utility functions and structs

template<typename Unsigned>
char * DigitsToString(Unsigned i, char * endPtr)
{
    char *&cursor(endPtr);
    *cursor = 0;
    if(!i)
    {
        *--cursor = '0';
        return cursor;
    }
    for(;i;i/=10)
    {
        *--cursor = char(i%10+'0');
    }
    return cursor;
}

template<typename Unsigned>
std::string UnsignedToString(Unsigned i)
{
    const size_t bufsize = std::numeric_limits<Unsigned>::digits10 + 1;
    char buffer[bufsize];
    return DigitsToString(i,buffer+bufsize-1);
}

template<typename Signed>
std::string SignedToString(Signed i)
{
    if(i>=0)
    {
        return UnsignedToString(i);
    }
    const size_t bufsize = std::numeric_limits<Signed>::digits10 + 2;
    char buffer[bufsize];
    char *cursor = DigitsToString(-i,buffer+bufsize-1);
    *--cursor = '-';
    return cursor;
}

template<bool is_signed,typename Integral>
struct ToStringConverter { };

template<typename Integral>
struct ToStringConverter<false,Integral>
{
    static std::string Do(Integral i){ return UnsignedToString(i); }
};

template<typename Integral>
struct ToStringConverter<true,Integral>
{
    static std::string Do(Integral i){ return SignedToString(i); }
};

// The IntToString()

template<typename Integral>
std::string IntToString(Integral i)
{
    return ToStringConverter<std::numeric_limits<Integral>::is_signed,Integral>::Do(i);
}

// Testing my IntToString()

template<typename Integral, Integral count>
void testFun()
{
    for(Integral i=0; i<count; ++i)
    {
        IntToString(i);
    }
}

// An std::stringstream based integer to string conversion

template<typename T>
std::string StreamedToString(const T& i)
{
    std::stringstream ss;
    ss << i;
    return ss.str();
}

// Testing of the std::stringstream based integer to string conversion

template<typename Integral, Integral count>
void testStream()
{
    for(Integral i=0; i<count; ++i)
    {
        StreamedToString(i);
    }
}

#ifndef TEST_CASE
#define TEST_CASE 1
#endif

int main()
{
#if TEST_CASE == 1
    testFun<uint64_t,100*1000*1000>();
#endif
#if TEST_CASE == 2
    testStream<uint64_t,100*1000*1000>();
#endif
    return 0;
}

This is far from being a market-ready product, but it might be useful. Speed test:

$ g++ -W -Wall -Wextra -pedantic -O2 int_to_str_speed2.cpp -DTEST_CASE=1 -o int_to_str_speed2
$ time ./int_to_str_speed2

real    0m12.404s
user    0m12.397s
sys     0m0.008s
$ g++ -W -Wall -Wextra -pedantic -O2 int_to_str_speed2.cpp -DTEST_CASE=2 -o int_to_str_speed2
$ time ./int_to_str_speed2

real    1m36.204s
user    1m36.174s
sys     0m0.008s

No comments:

Post a Comment