It took me a while to get it right, but here's my version, designed to take advantage of the recursive nature of the underlying algorithm. Oh, I
could done an iterative version that was more efficient and took less time to get right, but I just had to be
different, didn't I?
sigh Damn, I really need to comment this...
Code: Select all
#include <string.h>
const int maxbuffer = 33; // 32 bits + null terminator
char* u2string (unsigned int value, char* buffer, int radix)
{
unsigned int digit, reduction;
char tmp[maxbuffer];
const static char NUMERALS[16][2] = {"0", "1", "2", "3",
"4", "5", "6", "7",
"8", "9", "A", "B",
"C", "D", "E", "F"};
memset(tmp, '\00', (size_t) maxbuffer);
digit = value % radix;
reduction = value / radix;
if (0 != reduction)
{
strcat(buffer, u2string(reduction, tmp, radix));
}
strcat(buffer, NUMERALS[digit]);
return buffer;
}
char* i2string (int value, char* buffer, int radix)
{
char tmp[maxbuffer];
memset(tmp, '\00', (size_t) maxbuffer);
if ((0 > value) && (10 == radix))
{
value = -value;
buffer[0] = '-';
buffer[1] = '\00';
}
else
{
buffer[0] = '\00';
}
strcat(buffer, u2string((unsigned int) value, tmp, radix));
return buffer;
}
You probably wouldn't do it this way in the real world, of course. The chief headache I had with this was making sure the scratch buffer I was currently using didn't evaporate before I had concatenated the results into the caller's buffer; the big breakthrough came when I realized I could allocate the scratch buffer in the call
prior to the one using it, and pass it as the buffer argument. That way, when the call returned, the scratch buffer was still allocated, and could be appended to the current buffer, which was in turn the scratch buffer of it's caller, and so on. While it is incredibly inefficent in it's use of stack memory, because the function itself is much smaller, it probably doesn't take much more memory overall than the iterative version does, on average (the worst case scenario is a base 2 negative number, requiring 34 function calls with a frame overhead of ~48 bytes = 1632 bytes of stack space).
I also included the test program, just to show I didn't cheat (too much). There's still one small bug in the way u2string() handles zeroes, but other than that, it should work. C&CW.
Code: Select all
#include <stdio.h>
#include <stdlib.h>
// #include <conio.h> // Windows specific
int main( void )
{
char buffer1[(maxbuffer * 2) + 2], buffer2[(maxbuffer * 2) + 2];
int base, i;
memset(buffer1, '\00', (size_t) ((maxbuffer * 2) + 1));
memset(buffer2, '\00', (size_t) ((maxbuffer * 2) + 1));
for(base = 2; base <= 16; base += 2)
{
printf("%d in base %2d: %s == %s\n", 12765, base,
itoa(12765, buffer1, base),
i2string(12765, buffer2, base));
}
memset(buffer1, '\00', (size_t) ((maxbuffer * 2) + 1));
memset(buffer2, '\00', (size_t) ((maxbuffer * 2) + 1));
for(base = 2; base <= 16; base += 2)
{
printf("%d in base %2d: %s == %s\n", -254, base,
itoa(-254, buffer1, base),
i2string(-254, buffer2, base));
}
memset(buffer1, '\00', (size_t) ((maxbuffer * 2) + 1));
memset(buffer2, '\00', (size_t) ((maxbuffer * 2) + 1));
for(base = 2; base <= 16; base += 2)
{
printf("%d in base %2d: %s == %s\n", 0, base,
itoa(0, buffer1, base),
i2string(0, buffer2, base));
}
memset(buffer1, '\00', (size_t) ((maxbuffer * 2) + 1));
memset(buffer2, '\00', (size_t) ((maxbuffer * 2) + 1));
for(base = 2; base <= 16; base += 2)
{
printf("%d in base %2d: %s == %s\n", 0, base,
itoa(0, buffer1, base),
u2string(0, buffer2, base));
}
/*
// Windows specific function getch() used to pause
// console window in Dev-C++
puts("\nHit any key to continue...");
getch();
*/
}