Optimal Base-10 only itoa() function? [closed]

匿名 (未验证) 提交于 2019-12-03 01:12:01

问题:

In 20+ years programming in C I've used a base other than 10 once, so when I found my trusty MSVC's _itoa() missing in another environment, I set out to write one that only does base 10, and puts the destination buffer argument, pointing to the storage returned by the function, on the left, instead of on the right, like all of the string functions in the C Standard Library. I believe this code is also thread-safe.

Is there a faster way to do this?

I was also going to ask about correctness, but I believe the included test code proves it works, even for the particular case of LONG_MIN, which is (-1 * LONG_MAX) -1, which caused a failure in the code until I changed tactics, noted the sign, and then copied the signed int to an unsigned int. I then did all of the core work in the function in unsigned ints - which happily ran in 75% of the time as well.

char * _i32toa(char *const rtn, int32_t i)    {     if (NULL == rtn) return NULL;      // declare local buffer, and write to it back-to-front     char buff[12];     uint32_t  ut, ui;     char minus_sign=0;     char *p = buff + sizeof(buff)-1;     *p-- = 0;    // nul-terminate buffer      // deal with negative numbers while using an unsigned integer     if (i  9) {         ut = ui;         ui /= 10;         *p-- = (ut - (ui * 10)) + '0';     }     *p = ui + '0';      if ('-' == minus_sign) *--p = minus_sign;      // knowing how much storage we needed, copy chars from buff to rtn...     memcpy(rtn, p, sizeof(buff)-(p - buff));      return rtn; }  // ------------------------------------------------------------------------------------------------ #define LOOP_KNT (SHRT_MAX * 1024) // ------------------------------------------------------------------------------------------------ int main(void)    {     time_t start = clock();      int32_t t = 123456, i;     char *buff = (char *)malloc(256);      for (i = (SHRT_MIN *1024); i 

Performance is 2-4X that of the not-part-of-the-C-standard _itoa() in Visual Studio 2013, and 10-15X that of sprintf().

The approach is somewhat novel, and depends on knowing the required buffer size for the completed string - a problem the function allocating it's own string buffer, buff[] solves, making it thread-safe at the same time.

Knowing where the end of the buffer is allows the characters of the string to be written from the back to the front, solving the reverse order problem. The calling function doesn't need to prepare *rtn in any way, as the working string that gets memcpy()ed to *ptr is already null-terminated.

TVMIA for your feedback. The lack of a good _atoi() function is a persistent enough problem it deserves a good solution. Let's make one.

PS: On my i7 Hazwell box running MSVS C++ 64-bit with full optimizations, the full loop from LONG_MIN to LONG_MAX averages 116 clocks per conversion, for the round-trip, and only 28 clocks for _itoa(). That's over 725 megabytes per second of string - if comparing to Ben Voigt's code. I think I won Ben!

回答1:

You can eliminate the memcpy by writing directly into the caller's memory area.
You should have the caller pass the size of the buffer.

The other bottleneck is division, but I don't see how to get around that.

Edit 1: correct initialization of buffer pointer

char * _i32toa(char *const rtn, unsigned int buff_size, int32_t i)   {     if (NULL == rtn) return NULL;      uint32_t  ut, ui;     char minus_sign=0;     char *p = rtn + buff_size - 1;     // As before, without memcpy.     return rtn; } 


回答2:

Get rid of the auto char array and make them pass the size so you can check for overflow.

#define I32TOA( buff, val ) _i32toa( (buff), sizeof(buff), (val) )  char * _i32toa(char *const rtn, size_t size, int32_t i)    {     if (NULL == rtn) return NULL;      uint32_t  ut, ui;     char minus_sign=0;     char *p = rtn + size-1;     *p-- = 0;    // nul-terminate buffer     assert( p >= rtn );      if (i  9) {         ut = ui;         ui /= 10;         *p-- = (ut - (ui * 10)) + 48;         assert( p >= rtn );     }     *p = ui + 48;      if ('-' == minus_sign) {         *--p = minus_sign;         assert( p >= rtn );     }      return p; } 


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!