I was curious about the relative performance of these conversions so I did a few tests with the naive versions of a few methods. A few notes and caveats:
- The below conversion codes have no error checking and haven’t been tested (i.e., don’t copy/paste the code).
- Some methods only work for positive integers.
- The methods do not have equivalent output. For example numbers 0-9 (‘0.0’, ‘.0’, ‘.’) and factors of 10 (’10’, ’10.’, ‘10.0’). You should decide exactly what output you want for these cases.
- I would start by choosing the simplest algorithm and only optimize once it is found I need to. Do you really need performance of ~100 million integers/second?
- Unfortunately, I don’t have a C++11 compiler to test the
std::to_string()
versions.
Algorithms tested are:
string toString1 (const int a)
{
char buffer[32];
string result = itoa(a, buffer, 10);
char lastDigit = result[result.length() - 1];
result[result.length() - 1] = '.';
result += lastDigit;
return result;
}
void toString1a (string& Output, const int a)
{
char buffer[32];
Output = itoa(a, buffer, 10);
char lastDigit = Output[Output.length() - 1];
Output[Output.length() - 1] = '.';
Output += lastDigit;
}
string toString2 (const int a) {
float f = a * 0.1f;
ostringstream ss;
ss << f;
return ss.str();
}
const char* toString3 (const int a)
{
static char s_buffer[32]; //Careful!
itoa(a, s_buffer, 10);
size_t len = strlen(s_buffer);
char lastDigit = s_buffer[len-1];
s_buffer[len-1] = '.';
s_buffer[len] = lastDigit;
s_buffer[len+1] = '\0';
return s_buffer;
}
const char* toString4 (char* pBuffer, const int a)
{
itoa(a, pBuffer, 10);
size_t len = strlen(pBuffer);
char lastDigit = pBuffer[len-1];
pBuffer[len-1] = '.';
pBuffer[len] = lastDigit;
pBuffer[len+1] = '\0';
return pBuffer;
}
void toString4a (char* pBuffer, const int a)
{
itoa(a, pBuffer, 10);
size_t len = strlen(pBuffer);
char lastDigit = pBuffer[len-1];
pBuffer[len-1] = '.';
pBuffer[len] = lastDigit;
pBuffer[len+1] = '\0';
}
const char* toString5 (char* pBuffer, const int a)
{
snprintf(pBuffer, 16, "%.1f", a/10.0);
return pBuffer;
}
const char* toString6 (char* pBuffer, const int a)
{
snprintf(pBuffer, 16, "%d.%01d", a / 10, a % 10);
return pBuffer;
}
string toString7 (const int a)
{
ostringstream stream;
stream << (a / 10) << '.' << (a % 10);
return stream.str();
}
string toString8 (const int a)
{
char buffer[16];
string result = _itoa(a, buffer, 10);
result.insert(result.length()-1, 1, '.');
return result;
}
Basic benchmarks were run on the above methods for 100 million integers (starting from 0). If performance really matters for your case please profile/benchmark for yourself for your exact use-case.
- toString1 = 0.64 sec (itoa to string)
- toString1a = 0.61 sec (inplace, no return)
- toString2 = 33.4 sec (stringstream, ouch!)
- toString3 = 0.52 sec (itoa, static buffer)
- toString4 = 0.52 sec (itoa)
- toString4a = 0.52 sec (itoa, inplace)
- toString5 = 6.35 sec (snprintf, float)
- toString6 = 2.29 sec (snprintf, two ints)
- toString7 = 33.3 sec (stringstream, two ints)
- toString8 = 0.70 sec (itoa and string::insert)
Which one is “better” really depends on what you need. Personally I would use the simplest stream case (toString7) unless I was absolutely sure I needed the performance of the other methods.
3
solved convert int (which actually decimal) to string (1234 to “123.4”)