1

I am trying to make a C program for converting a given number in say base x, to base y. I chose to narrow it down upto base 20 (i.e. Base 2 to 20). When it comes to scanning a hexadecimal number (includes ABCDEF too, right?) for example, I am stuck. Please look at my program below:

/* NOTE: This program uses two step approach to convert a given number in any base (except base 10, in which case we will use only "toany()") to any other base*/
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
int inum,ibase, obase;
int todec(); //function to convert to decimal from any base
int toany(int); //function to convert from decimal to any base
int exp(int,int); //used in other function 
void main()
{
    int num,choice;
    char strr[100];
    enum{A=10,B,C,D,E,F,G,H,I,J};
    here:
    printf("Enter the base (RADIX) of your number: ");
    scanf("%d",&ibase);
    printf("Enter the number in base %d: ",ibase);
    scanf("%s",strr);
    printf("Enter the base in which you want the output: ");
    scanf("%d",&obase);
    inum=atoi(strr);
    switch(obase)
    {
        case 10:
        num=todec();
        printf("Output in base 10: %d\n",num);
        break;
        default:
        if(ibase==10)
            num=toany(inum);
        else
            num=toany(todec());
        printf("Output in base %d: %d\n",obase,num);
        break;
    }
    printf("WANNA DO IT AGAIN? If yes, Press 1 else press 0:");
    scanf("%d",&choice);
    if(choice==1)
        goto here;
    else
        exit(0);
    getch();
}
int exp(int p, int q)
{
    int i,result=1;
    for(i=1;i<=q;i++)
    {
        result=result*p;
    }
    return(result);
}
int todec()
{
    int inumarr[100],dupnum=inum,i=0,counter,decnum=0;  
    while(dupnum!=0)
    {
        inumarr[i]=dupnum%10;
        dupnum/=10;
        i++;
    }
    for(counter=0;counter<i;counter++)
    {
        decnum=decnum+inumarr[counter]*exp(ibase, counter);
    }
    return(decnum);
}
int toany(int num)
{
    int outnumarr[100],i=0,q,result=0;
    while(num!=0)
    {
        outnumarr[i]=num%obase;
        num=num/obase;
        i++;
    }
    for(q=0;q<i;q++)
    {
        result=result+outnumarr[q]*exp(10,q);
    }
    return(result);
}

Thanks for reading! Now, I know it's definitely a mess where I tried to scan as a string and then applied atoi function on a string that might contain alphabets (like "19E" in base 16...which is 414 in base 10). So, I am looking for a decent solution which will allow the user of this program to enter any number like "19E" and my program will interpret that 'E' as 14 (AS DEFINED IN MY ENUM) and also a decent way to show an output of numbers like "19E" would be great.

1 Answers1

0

Disclaimer: The code I've put into this answer is untested. I'm currently on a mobile device, so even compiling it is less convenient than usual. I will strive to include enough details for you to find your way past any (possible) errors, please point them out though... On another day I'll polish this post off by adding more checks (described at the end) and explain serialisation as well as deserialisation. As it stands, however, it seems you're just asking about deserialisation, so without further adeau:

Build a lookup table of some description containing each character from your base. For characters then you can (usually) get away with using string operations. For example:

unsigned char hex_digit[] = "00112233445566778899AaBbCcDdEeFf";

If you use strchr and some pointer arithmetic you can now find the offset of a character, divide by two to reduce it to a value within 0 .. 15, or modulo by two to discriminate between lowercase and uppercase.

You can devise any base like this, with a generic loop parsing the input to facilitate larger values...

size_t to_native_uimax(char *str, unsigned char *base, uintmax_t *value) {
    size_t x, base_size = strlen(str);
    uintmax_t v = 0;
    for (x = 0; str[x]; x++) {
        unsigned char *c = strchr(base, str[x]);
        if (!c) break;

        v *= base_size / 2;
        v += (c - base) / 2;
    }
    *value = v;
    return x;
}

Signage is a bit trickier to handle, but because we only need to handle the sign at the start of the string we can reuse the code above.

size_t to_native_imax(unsigned char *str, unsigned char *base, intmax_t *value) {
    uintmax_t v = 0;
    size_t x = to_native_uimax(str + !!strchr("-+", *str), base, &v);
    *value = *str == '-' ? -(intmax_t)v : v;
    return x;
}

Also note that this code isn't strictly portable; if it's possible that this might be deployed to a system that has negative zeros or signals on overflow more checks should precede the (intmax_t) conversion.

autistic
  • 1
  • 3
  • 35
  • 80