Discussion:
itoa question
(too old to reply)
Mat Nieuwenhoven
2012-08-18 17:41:16 UTC
Permalink
I 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
Lynn McGuire
2012-08-20 16:26:32 UTC
Permalink
Post by Mat Nieuwenhoven
I 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
First, the optimizer in OW C, C++ and F77 is quite
good. Very good in fact. It just does not use
the matrix functions that were introduced back in
the 1990s.

Second, the itoa function that you wrote/found may
not be quite as fully featured or have as many
protections in it. If at all.

Without a detailed analysis here, this is all a
shot in the dark. However, assembly language is
not a direct match one to one on to machine
language. And the OW optimizer does not optimize
assembly language.

In these situations, I tend to write my own
function to get over shortcomings in the default
library. Something like
std::string asString (int i);
std::string asString (double d);

Lynn
Paul S. Person
2012-08-21 17:05:32 UTC
Permalink
On Sat, 18 Aug 2012 19:41:16 +0200 (CES), "Mat Nieuwenhoven"
Post by Mat Nieuwenhoven
I 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?
Since nobody has come up with an answer, here is a guess!

I suspect it is because all bases different from 10 are taken to be
used only by Programmers to represent Bit Configurations, and so sign
is an irrelevant concept.

To put it another way: negative values are grudgingly allowed for base
10 as hoi polloi use base ten numbers and will, not being Programmers,
expect them to, from time to time, have negative values.
--
"Nature must be explained in
her own terms through
the experience of our senses."
Hans-Bernhard Bröker
2012-08-21 20:15:17 UTC
Permalink
Post by Mat Nieuwenhoven
I 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?
I think the Watcom or OW developers are the wrong people to ask. OW's
itoa() behaves the same way as at least two other C libraries'
documentation (MSVC, DJGPP) specifies it. Lacking an actual standard
that would specify the behaviour, such compatibility to prior "art" can
be the only guide there is.

It could also be that this was supposed to reflect on the ancient
semantics of numeric constants in pre-standard C. Before ANSI C
introduced the 'u' suffix, spelling it out in hex or octal was the only
way to create an unsigned integer constant.
Post by Mat Nieuwenhoven
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.
To make that a meaningful comparison, you should at least split it up
into two files. For all you know, those 5 seconds of difference could
well be just function call overhead compared to an inlined version.
Mat Nieuwenhoven
2012-08-23 04:40:20 UTC
Permalink
Post by Hans-Bernhard Bröker
Post by Mat Nieuwenhoven
I 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?
I think the Watcom or OW developers are the wrong people to ask. OW's
itoa() behaves the same way as at least two other C libraries'
documentation (MSVC, DJGPP) specifies it. Lacking an actual standard
that would specify the behaviour, such compatibility to prior "art" can
be the only guide there is.
I agree here. Compatibility is important.
Post by Hans-Bernhard Bröker
It could also be that this was supposed to reflect on the ancient
semantics of numeric constants in pre-standard C. Before ANSI C
introduced the 'u' suffix, spelling it out in hex or octal was the only
way to create an unsigned integer constant.
Post by Mat Nieuwenhoven
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.
To make that a meaningful comparison, you should at least split it up
into two files. For all you know, those 5 seconds of difference could
well be just function call overhead compared to an inlined version.
Now there's a thought. I will try that if I have a moment. I have posted my
test code in another post.

Mat Nieuwenhoven
Wilton Helm
2012-08-23 02:35:36 UTC
Permalink
I would agree with what has already been suggested. The only other bases
with more than idiosyncratic use are 2, 8, 16 which, of course are used
almost exclusively for bit related purposes. While one can define negative
representation for them it is rarely useful. So essentially itoa assumes
the data is signed int for decimal and unsigned int for everything else.
While that isn't particularly consistent, it is pragmatic.

Lynn's comment about features and protections is also very appropriate. I
sometimes write my own stuff because I don't need certain features, or know
that certain problems can't occur. One obvious example would be ability to
handle wide-characters (which I have no use for).

Wilton
Mat Nieuwenhoven
2012-08-23 04:43:50 UTC
Permalink
Post by Mat Nieuwenhoven
I 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;
}

Loading...