I/O in [Almost] Any Base

After climbing the ternary I/O mountain, and crafting functions that both input and output base 3 values, the next step is obvious: Combine both functions into a single program. The step after that is less obvious: Change the code so that any base can be used to process input or generate output.

Well, almost any base.

As the functions are written, then work best to translate bases that use digits 0 through 9. I assume base 11 would use ‘A’ to represent the decimal 10, just as hexadecimal does. This modification could be made to my code, but I was in a hurry to see whether it worked.

From last week’s Lesson, I crafted the base_in() function.

From the week before’s Lesson, I created the base_out() function.

To make both work with any base (as opposed to base 3), I created the defined constant BASE, which can be set to a value from 2 to 9:

#define BASE 5

The changes in each function include building the powers table:

/* create the powers table */
    powers[0] = 1;
    for(x=1;x<12;x++)
    {
        powers[x] = powers[x-1]*BASE;
    }

And the test for a valid digit in the input function:

/* bail on invalid digit */
        if( *(t+x)<'0' || *(t+x)>(BASE+'0') )
            return(-1);

Otherwise, the functions are pretty-much the same as shown in earlier Lessons for my ternary I/O functions. Here is the full code, which works with base 5:

2020_06_27-Lesson.c

#include <stdio.h>
#include <stdlib.h>

#define BASE 5

/* translate value to string */
char *base_out(unsigned n)
{
    static char tstring[12];
    char *t;
    int powers[12];
    int x,r;

    /* check for overflow */
    if( n<0 || n>65535 )
    {
        fprintf(stderr,"%d is out of range\n",n);
        exit(1);
    }

    /* create the powers table */
    powers[0] = 1;
    for(x=1;x<12;x++)
    {
        powers[x] = powers[x-1]*BASE;
    }

    /* build the return string */
    tstring[11] = '\0';
    for(x=0;x<11;x++)

        r = n % powers[x+1];
        n -= r;
        tstring[10-x] = r/powers[x] + '0';
    }

    /* remove any leading zeros */
    t = tstring;
    x = 0;
    while( *t=='0' && x<10 )

        t++;
        x++;
    }

    return(t);
}

/* translate string to value */
unsigned base_in(char *t)
{
    int powers[11];
    int len,x,r,p;

    /* create the powers table */
    powers[0] = 1;
    for(x=1;x<11;x++)
    {
        powers[x] = powers[x-1]*BASE;
    }

    /* translate the string */
        /* find the end of the string */
    len = 0;
    while(1)
    {
        if( *(t+len)=='\n' || *(t+len)=='\0' )
        {
            break;
        }
        len++;
        /* limit to 11 digits */
        if( len==11 )
            break;
    }
    /* backup over the null character */
    len--;

    /* process the string backward
       but process the powers table forward */
    r = 0;
    for( x=len,p=0 ; x>=0 ; x--,p++)
    {
        /* bail on invalid digit */
        if( *(t+x)<'0' || *(t+x)>(BASE+'0') )
            return(-1);
        r += ( (*(t+x)-'0') * powers[p] );
    }

    return(r);
}

int main()
{
    unsigned b;
    char *bstring;

    printf("Base %d I/O\n",BASE);
    /* prompt for input */
    printf("Enter a decimal value: ");
    scanf("%d",&b);

    bstring = base_out(b);
    printf("%d in base %d is %s\n",b,BASE,bstring);
    printf("%s in decimal is %d\n",bstring,base_in(bstring));

    return(0);
}

The code outputs the base in the main() function, then prompts for an input value in decimal. This value is translated into the named base for output, then the base-whatever value string is translated back into decimal, proving that the thing works.

Here’s your sample run:

Base 5 I/O
Enter a decimal value: 12345
12345 in base 5 is 343340
343340 in decimal is 12345

Improvements to the code might include making the powers table dynamic. For example, the higher the base the more astronomical the values in the powers table. Further, this value can be keyed to an input cap for the base_out() function. In the current code, the input cap is set at Line 14 to 65535.

Another obvious improvement would be to properly handle bases higher than 9. I almost made this improvement, but figured it would be a good exercise for you to try on your own.

Leave a Reply