title: Optimal Base-10 only itoa() function?
body: <p>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. </p>
  
  <p><strong>Is there a faster way to do this?</strong> </p>
  
  <p>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.</p>
  
  <pre><code>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 &lt; 0)    {
          minus_sign = '-';
          ui = (uint32_t)((int)-1 * (int)i);
      }    else    {
          ui = i;
      }
  
      // core code here...
      while (ui &gt; 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 &lt; LOOP_KNT; i++)    {
          _i32toa(buff, i);
      }
      printf("\nElapsed time was %f milliseconds", (double)clock() - (double)(start));
  
      start = clock();
      for (i = (SHRT_MIN * 1024); i &lt; LOOP_KNT; i++)    {
          _itoa(i, buff, 10);
      }
      printf("\nElapsed time was %f milliseconds", (double)clock() - (double)(start));
  
      start = clock();
      for (i = (SHRT_MIN * 1024); i &lt; LOOP_KNT; i++)    {
          ___itoa(i, buff, 10);
      }
      printf("\nElapsed time was %f milliseconds", (double)clock() - (double)(start));
  
      printf("\nString from integer %i is %s\n", t, _i32toa(buff, t));
      printf("\nString from integer %i is %s\n", -0, _i32toa(buff, -0));
      printf("\nString from integer %i is %s\n", -1, _i32toa(buff, -1));
      printf("\nString from integer %i is %s\n", LONG_MIN, _i32toa(buff, LONG_MIN));
  
      start = clock();
      for (int i = LONG_MIN; i &lt; LONG_MAX; i++) {
          if (i != atoi(_i32toa(buff, (int32_t)i))) {
              printf("\nError for %i", i);
          }
          if (!i) printf("\nAt zero");
      }
      printf("\nElapsed time was %f milliseconds", (double)clock() - (double)(start));
  
      getchar();
      return 0;
  }
  </code></pre>
  
  <p>Performance is 2-4X that of the not-part-of-the-C-standard _itoa() in Visual Studio 2013, and 10-15X that of sprintf(). </p>
  
  <p>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. </p>
  
  <p>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. </p>
  
  <p>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.</p>
  
  <p><strong>PS:</strong> 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!</p>