Post by Mat NieuwenhovenI recently needed itoa for base 36. I discovered that for negative numbers
OpenWatcom's itoa delivers a positive result, except when the base is 10.
So -1 will only be -1 in text for base 10, not e.g. for base 9.
This behaviour is documented.
I wonder what the reasoning behind this is. Any ideas?
Another point is that when I examined an alternative written in C (OW libs
implemenattion is in assembly), that the C alternative was much quicker.
A test that ran for 15 seconds with the OW lib implementation took only 10
with the C implementation, implemented as a subroutine in the same source
file. I find this quite strange.
Mat Nieuwenhoven
Below is the code I was testing with. Rename to itoa_test.c.
/*********************************************************
*
* Test various itoa implementations.
* For OpenWatcom, compile with "wcl386 itoa_test -i=%watcom%\h -otax"
*
*********************************************************/
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
int main(int argc, char *argv[]);
char *itoa_GPL3(int value, char* result, int base);
char *itoa_public(int value, char* result, int base);
int main(int argc, char *argv[])
{
int i, base;
char *p;
char bufferOW[8 * sizeof(int) + 2];
char bufferGPL3[8 * sizeof(int) + 2];
char bufferPublic[8 * sizeof(int) + 2];
int increment = 1000;
int errors;
time_t StartTime, EndTime;
// First, do the test using OpenWatcom's lib version
StartTime = time(NULL);
printf("\nTesting OW atio\nbase:");
for (base = 2; base <= 36; base++)
{
errors = 0;
if (base == 2)
printf("%d", base);
else
printf(",%d", base);
flushall(); // Show the output
for (i = INT_MIN; i <= (INT_MAX - increment); i += increment)
{
p = itoa(i, bufferOW, base);
if (!p
|| p[0] == 0 && i != 0)
{
if (++errors > 1) // Allow 1 error (INT_MIN))
break;
}
}
if (errors < 2 // Make sure very last value is tested too
&& i != INT_MAX)
{
p = itoa(INT_MAX, bufferOW, base);
if (!p
|| p[0] == 0 && i != 0)
{
if (++errors > 1) // Allow 1 error (INT_MIN))
break;
}
}
}
EndTime = time(NULL);
printf("\nRun time for OW lib atoi %d seconds\n", EndTime - StartTime);
// Secondly, do the test using an GPL3 source version found on the web
StartTime = time(NULL);
printf("\nTesting GPL3 atio\nbase:");
for (base = 2; base <= 36; base++)
{
errors = 0;
if (base == 2)
printf("%d", base);
else
printf(",%d", base);
flushall(); // Show the output
for (i = INT_MIN; i <= (INT_MAX - increment); i += increment)
{
p = itoa_GPL3(i, bufferGPL3, base);
if (!p
|| p[0] == 0 && i != 0)
{
if (++errors > 1) // Allow 1 error (INT_MIN))
break;
}
}
if (errors < 2
&& i != INT_MAX)
{
p = itoa(INT_MAX, bufferGPL3, base);
if (!p
|| p[0] == 0 && i != 0)
{
if (++errors > 1) // Allow 1 error (INT_MIN))
break;
}
}
}
EndTime = time(NULL);
printf("\nRun time for GPL3 atoi %d seconds\n", EndTime - StartTime);
// Thirdly, do the test using an public domain source version found on
the web
StartTime = time(NULL);
printf("\nTesting public atio\nbase:");
for (base = 2; base <= 36; base++)
{
errors = 0;
if (base == 2)
printf("%d", base);
else
printf(",%d", base);
flushall(); // Show the output
for (i = INT_MIN; i <= (INT_MAX - increment); i += increment)
{
p = itoa_public(i, bufferPublic, base);
if (!p
|| p[0] == 0 && i != 0)
{
if (++errors > 1) // Allow 1 error (INT_MIN))
break;
}
}
if (errors < 2
&& i != INT_MAX)
{
p = itoa(INT_MAX, bufferOW, base);
if (!p
|| p[0] == 0 && i != 0)
{
if (++errors > 1) // Allow 1 error (INT_MIN))
break;
}
}
}
EndTime = time(NULL);
printf("\nRun time for public domain atoi %d seconds\n\n", EndTime -
StartTime);
// Fourthly, compare the outputs.
// OW doesn't prepend a - sign if the base is not 10???
for (base = 2; base <= 36; base++)
{
errors = 0;
printf("testing base %d\n", base);
for (i = INT_MIN; i <= (INT_MAX - increment); i += increment)
{
itoa(i, bufferOW, base);
itoa_GPL3(i, bufferGPL3, base);
itoa_public(i, bufferPublic, base);
if (strcmp(bufferOW, bufferGPL3) != 0
|| strcmp(bufferOW, bufferPublic) != 0)
{
printf("itoa mismatch: base %d, value decimal %i = hex
%X\n\tOW : %s\n\tGPL3 : %s\n\tpublic: %s\n",
base, i, i, bufferOW, bufferGPL3, bufferPublic);
if (++errors > 1) // allow INT_MIN error
break;
}
}
}
return 0;
}
/***********************************************************
* C++ version 0.4 char* style "itoa":
* Written by Lukßs Chmela
* Released under GPLv3.
************************************************************/
char *itoa_GPL3(int value, char* result, int base)
{
char *ptr = result,
*ptr1 = result,
tmp_char;
int tmp_value;
// check that the base if valid
if (base < 2 || base > 36)
{
*result = '\0';
return result;
}
do
{
tmp_value = value;
value /= base;
*ptr++ =
"zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz"[35
+ (tmp_value - value * base)];
} while ( value );
// Apply negative sign
if (tmp_value < 0) *ptr++ = '-';
*ptr-- = '\0';
while(ptr1 < ptr)
{
tmp_char = *ptr;
*ptr--= *ptr1;
*ptr1++ = tmp_char;
}
return result;
}
/***************************************************************
* itoa code not covered by GPL3
*
http://www.daniweb.com/software-development/c/threads/148080/itoa-function-o
r-similar-in-linux
* Reformatted and commented for clarity, and modified with
* extra checks, and allow negative values for non-base10.
* Fixed string reversal
***************************************************************/
/* The Itoa code is in the puiblic domain */
char* itoa_public(int value, char* result, int base)
{
static char dig[] = "0123456789abcdefghijklmnopqrstuvwxyz";
int n = 0, neg = 0;
unsigned int v;
char *p, *q;
char c;
// check that the base if valid
if (base < 2 || base > 36)
{
*result = '\0';
return result;
}
if (value < 0)
{
neg = 1;
value = -value;
}
v = value;
do
{
result[n++] = dig[v%base];
v /= base;
} while (v);
if (neg)
result[n++] = '-';
result[n] = '\0';
// Doing pointer changes not in the for() is slightly faster
for (p = result, q = p + n - 1; p < q; )
c = *p, *p++ = *q, *q-- = c;
return result;
}