Effectively convert unsigned short to char *

What would be an efficient portable way of converting unsigned short to char * (i.e., converting from 25 to 25).

I would like to avoid things like involving strings (std :: string). Performance is important in this case, because this conversion must occur quickly and often.

I was looking for things like using sprintf, but would like to research any ideas.

+6
c ++ char unsigned short
source share
5 answers

First of all, do it right, then do it quickly - just optimize if you can make sure that the code snippet is not working.

snprintf() to the buffer will do what you want. Is this the fastest solution? It's my pleasure. But it is one of the simplest, and this is enough to make your code work in working condition. From there, if you see that these snprintf() calls are so time-consuming that they need to be optimized, then and only then look for a faster solution.

+6
source share

An array of strings for which

 array[25] = "25"; array[26] = "26"; array[255] = "255"; 

may be? You can write a small program that easily generates the source code for the table, and then use this file in your project.

Edit: I do not understand what you mean, you do not want the involved strings to be linked.

+2
source share

I would say at least try sprintf, and since you have this tag as C ++, try StringStream and actually profile them. In many cases, the compiler is smart enough to create something that works very well. Only when you know that this will be a bottleneck, you need to find a faster way.

+1
source share

try the following:

 int convert(unsigned short val, char* dest) { int i = 0; if (val > 10000) { dest[i++] = (val / 10000) | 0x30; val %= 10000; } if (val > 1000) { dest[i++] = (val / 1000) | 0x30; val %= 1000; } if (val > 100) { dest[i++] = (val / 100) | 0x30; val %= 100; } if (val > 10) { dest[i++] = (val / 10) | 0x30; val %= 10; } dest[i++] = (val) | 0x30; dest[i] = 0; return i; } 
+1
source share

I put together some tests here, and here is what I came up with:

write_ushort: 7.81 s
uShortToStr: 8.16 s
translation: 6.71 s
use_sprintf: 49.66 s

(Write_ushort is my version, which I tried to write as clearly as possible, and not micro-optimize, for formatting to a given character buffer; use_sprintf is the obvious sprintf (buf, "% d", x) and nothing else, the other two taken from other answers here.)

This is a pretty amazing difference between the two, right? Who would have thought to use sprintf when faced with almost different sizes? Oh yes, how many times have I repeated each function tested?

 // Taken directly from my hacked up test, but should be clear. // Compiled with gcc 4.4.3 and -O2. This test is interesting, but not authoritative. int main() { using namespace std; char buf[100]; #define G2(NAME,STMT) \ { \ clock_t begin = clock(); \ for (int count = 0; count < 3000; ++count) { \ for (unsigned x = 0; x <= USHRT_MAX; ++x) { \ NAME(x, buf, sizeof buf); \ } \ } \ clock_t end = clock(); \ STMT \ } #define G(NAME) G2(NAME,) G2(NAME,cout << #NAME ": " << double(end - begin) / CLOCKS_PER_SEC << " s\n";) G(write_ushort) G(uShortToStr) G(convert) G(use_sprintf) #undef G #undef G2 return 0; } 

Sprintf turned the entire possible range of unsigned shorts, and then the entire range again increased by 2999 times by about 0.25 Ξs per average conversion on my laptop with a 5-year year.

Sprintf is portable; Is it also effective enough for your requirements?


My version:

 // Returns number of non-null bytes written, or would be written. // If ret is null, does not write anything; otherwise retlen is the length of // ret, and must include space for the number plus a terminating null. int write_ushort(unsigned short x, char *ret, int retlen) { assert(!ret || retlen >= 1); char s[uint_width_10<USHRT_MAX>::value]; // easy implementation agnosticism char *n = s; if (x == 0) { *n++ = '0'; } else while (x != 0) { *n++ = '0' + x % 10; x /= 10; } int const digits = n - s; if (ret) { // not needed by checking retlen and only writing to available space //assert(retlen >= digits + 1); while (--retlen && n != s) { *ret++ = *--n; } *ret = '\0'; } return digits; } 

The compilation functions of the TMP protocol are not new, but include this complete example, because this is what I used:

 template<unsigned N> struct uint_width_10_nonzero { enum { value = uint_width_10_nonzero<N/10>::value + 1 }; }; template<> struct uint_width_10_nonzero<0> { enum { value = 0 }; }; template<unsigned N> struct uint_width_10 { enum { value = uint_width_10_nonzero<N>::value }; }; template<> struct uint_width_10<0> { enum { value = 1 }; }; 
+1
source share

All Articles