5

I know about 'strtoll' but that is converting any Radix Base Number (between 2 and 36) to Decimal and I need to do the opposite by converting Decimal to Any Radix Base Number,

an example would be decimal 130 radix 12 = AA

flylib
  • 1,128
  • 2
  • 10
  • 17
  • If you have the substring function in C, it is straightforward, isn't it? – Manikandan Sigamani Sep 28 '13 at 22:34
  • `C` has ternary comparison operator as well. There were no need to create the whole block. – PM 77-1 Sep 28 '13 at 22:35
  • If this is not a homework requirement, and you just want to convert a string representation of a number to another base, you can use `strtoull()` – ryyker Sep 28 '13 at 22:36
  • the compiler I have to use doesn't support that method and I have to do it out manually – flylib Sep 28 '13 at 23:03
  • Your best bet is to use a standard library function like strtoll(). Q: Why can't you do this? Q: If your compiler doesn't support it, what *is* your compiler? Perhaps we can suggest a workaround. – paulsm4 Sep 28 '13 at 23:14
  • ok it does work but that's converting any Radix Base Number (between 2 and 36) to Decimal and I need to do the opposite by converting Decimal to Any Radix Base Number so an example would be decimal 130 radix 12 = AA – flylib Sep 28 '13 at 23:22
  • You are partly right. It converts a string in any radix to binary. It isn't the method you're need. – user207421 Sep 28 '13 at 23:40
  • @EJP - Just curious, strotoull converts from string representations of numbers in radix from 0 to 36 to `unsigned long long`, Is that the same as saying _It converts a string in any radix to binary_? i.e. I know it is stored as binary in memory, but it is also stored _as_ `unsigned long long`, and is represented easily as a base 10 number. – ryyker Sep 29 '13 at 23:39
  • Answered [here](https://stackoverflow.com/a/71893738/18765627), converted **Fibonacci(1500000)** for testing. – Michel Apr 16 '22 at 12:17

3 Answers3

6

The following code does it using a temporary buffer to build the string, then returning a duplicate. The string is built by working backwards from the end and setting each digits by indexing into another string. That should be much more efficient than all that repeated short string copying and appending the Java version does. You need to free the returned strings when you're done with them and check for NULL return if there's any chance the radix might be out of range... to avoid allocating new strings all the time you might want to adapt it to use a buffer you supply for the result.

/* return string representation of num in base rad as new string (or NULL) */
char *toBaseWhatever(int num, int rad)
{
    char digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
    int i;
    char buf[66];   /* enough space for any 64-bit in base 2 */

    /* bounds check for radix */
    if (rad < 2 || rad > 62)
        return NULL;
    /* if num is zero */
    if (!num)
        return strdup("0");

    /* null terminate buf, and set i at end */
    buf[65] = '\0';
    i = 65;

    if (num > 0) {  /* if positive... */
        while (num) { /* until num is 0... */
            /* go left 1 digit, divide by radix, and set digit to remainder */
            buf[--i] = digits[num % rad];
            num /= rad;
        }
    } else {    /* same for negative, but negate the modulus and prefix a '-' */
        while (num) {
            buf[--i] = digits[-(num % rad)];
            num /= rad;
        }
        buf[--i] = '-';
    }   
    /* return a duplicate of the used portion of buf */
    return strdup(buf + i);
}
Dmitri
  • 9,175
  • 2
  • 27
  • 34
  • so when trying to call it in a main method, I want to create a char answer[] then do something like this, answer = toBaseWhatever(decimal, radix); it compiles and gives me weird incorrect numbers? – flylib Sep 29 '13 at 01:22
  • No.. in main you'd create a `char *answer` and do `answer = toBaseWhatever(decimal, radix);`. Then when you're done with the result do `free(answer);`. – Dmitri Sep 29 '13 at 01:28
  • okay thanks I agree your solution is cleaner then the other guy and gave you the correct answer but again thanks both of you for the help, you both gave quality solutions – flylib Sep 29 '13 at 02:29
2

If it is not required that you create you own, 'strtoll' or 'strtoull' will do a conversion for you:

   long long           i64 = 0x8000000000000000LL;
    unsigned long long  u64 = 0xFFFFFFFFFFFFFFFFULL;

    printf("Signed long long:   %lld\n", i64);
    printf("Unsigned long long: %llu\n", u64);
    printf("\n");

    char buffer[30];
    sprintf(buffer, "%lld", i64);
    i64 = atoll(buffer);
    i64 = strtoll(buffer, NULL, 10);
    u64 = strtoull(buffer, NULL, 10);  

[EDIT] This edit is in response (purely for fun) to @willus comments regarding timing between his and @dmitri's solutions, and adds a third into the mix.
In terms of pure speed, Dmitri's offering wins hand's down, but includes memory leaks. It uses the strdup() function in such a way where the memory is not freed. strdupa() is an alternative that frees its allocated memory, and would be a good replacement here. And, if strdup() or strdupa() were not used at all, @Dmitri's code (as pointed out by @willus) would run even faster.

Credit to this implementation for the AnyRadixConvert functions.

Here are the resuts for each of the three running through integers 0 - 5001:
enter image description here

Here is the code that produced these results: (_note: minor changes exist eg. where i did not have strdup(), but had StrDup())

#include "toolbox.h" //StrDup(), delete otherwise
#include <ansi_c.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

#define MAXLEN_127 127

void delaySome(void);  //test the timers...

int AnyRadixConvert(const char* oldNumber, int oldRadix,
                           char* newNumber, int newRadix);

///Willus
static void willus_decimal_to_radix_base(char *s, int x,int base,int maxlen);
static void insert_char(char *s,int c,int maxlen);
static int digit(int x);

////Dmitri
char *dmitri_toBaseWhatever(int num, int rad);


int main(void)
{
   char dataIn[66]="5001";
   char dataOut[66];
   int radixFrom = 10;
   int radixTo = 2;
   long long int i=0;

   //support willus_decimal_to_radix_base
    int x;

    //support Dmitri
   char demitri_result[66];
   demitri_result[0]=0;
   int dataInNum;
   int maxInAll = 5001;

    /*Converts from base x to base y*/  //values from 2 to 36 for either parameter
    //time it
    // Delay(1);
    clock_t start, end;
    long double elapsed1, elapsedWillus, elapsedDimitri;
    start = clock();
    //end time it

    for(i=0;i<=maxInAll;i++)
    {
        sprintf(dataIn, "%lld", i);
        AnyRadixConvert(dataIn,radixFrom,dataOut,radixTo);
    }
    //end timeit
    end = clock();
    elapsed1 = ((double) (end - start)) / CLOCKS_PER_SEC;
    printf("AnyRadixConvert:\nFrom base %d: %s\nTo base %ld: %s\nElapsed time: %3.15Lf\n\n"             ,radixFrom,dataIn,radixTo, dataOut, elapsed1);

//willus_decimal_to_radix_base

    x=atol(dataIn);
    dataOut[0]=0;
    start = clock();
    for(i=0;i<=maxInAll;i++)
    {
        willus_decimal_to_radix_base(dataOut,(int)i,radixTo,MAXLEN_127);
    }
    end = clock();
    elapsedWillus = ((double) (end - start)) / CLOCKS_PER_SEC;
    printf("willus_decimal_to_radix_base:\nFrom base %d: %s\nTo base %ld: %s\nElapsed time: %3.15Lf\n\n",radixFrom,dataIn,radixTo, dataOut, elapsedWillus);

//dimitri_toBaseWhatever    

    dataInNum = atol("123123132");
    start = clock();
    for(i=0;i<=maxInAll;i++)
    {
        strcpy(demitri_result, dmitri_toBaseWhatever((int)i, radixTo));
    }

    end = clock();
    elapsedDimitri = ((double) (end - start)) / CLOCKS_PER_SEC;
    printf("dimitri_toBaseWhatever:\nFrom base %d: %s\nTo base %ld: %s\nElapsed time: %3.15Lf\n\n"      ,radixFrom,dataIn,radixTo, demitri_result, elapsedDimitri);

   //////////////////////////
    getchar();
    return 0;
}

int AnyRadixConvert(const char* oldNumber, int oldRadix, char* newNumber, int newRadix)
{
    long dataIn = 0L;
    char digit = 0;
    int i = 0;
    int size=strlen(oldNumber);
    char* reverseNew = NULL;

    /*Checks if base if within bounds*/
    if((oldRadix<=1 || oldRadix>36)||(newRadix<=1 || newRadix>36))
    {
        return 0;
    }
    /*Convert to base 10 from old base*/
    for(i=0;i<size;i++)
    {
        if(oldNumber[i]>='0' && oldNumber[i]<='9')
        {
            dataIn+=(oldNumber[i]-'0')*pow(oldRadix,size-i-1);
        }
        else if(oldNumber[i]>='A' && oldNumber[i]<='Z')
        {
            dataIn+=(oldNumber[i]-'A' + 10)*pow(oldRadix,size-i-1);
        }
        else
        {
            return -1;
        }
    }
    i=0;
    /*Convert from base 10 to new base*/
    while(dataIn>0)
    {
        digit = dataIn % newRadix;
        (digit<10)?(newNumber[i] = digit + '0')
                  :(newNumber[i] = digit + 'A' -10);
        dataIn=dataIn/newRadix;
        i++;
    }
    newNumber[i]='\0';
    /*Reverses newNumber*/
    reverseNew = (char*)(malloc(sizeof(char)*strlen(newNumber)+1));
    reverseNew[0]=0;
    size = strlen(newNumber);
    for(i=size-1; i>=0; i--)
    {
        reverseNew[i] = newNumber[size-1-i];
    }
    reverseNew[size]='\0';
    strcpy(newNumber,reverseNew);
    free(reverseNew);
    return 1;
}


static void willus_decimal_to_radix_base(char *s, int x,int base,int maxlen)

    {
    int sign;

    sign= (x<0) ? -1 : 1;
    if (x<0)
        x=-x;
    s[0]='\0';
    if (x==0)
        {
        if (maxlen > 0)
            strcpy(s,"0");
        return;
        }
    while (x>0)
        {
        int r;
        r=x%base;
        insert_char(s,digit(r),maxlen);
        x /= base;
        }
    if (sign<0)
        insert_char(s,'-',maxlen);
    }

static void insert_char(char *s,int c,int maxlen)

    {
    int len;

    len=strlen(s);
    if (len>=maxlen)
        memmove(&s[1],s,len-1);
    else
        memmove(&s[1],s,len+1);
    s[0]=c;
    }


static int digit(int x)

    {
    if (x<10)
        return('0'+x);
    return('A'+x-10);
    }

////Dimitri
/* return string representation of num in base rad as new string (or NULL) */
char *dmitri_toBaseWhatever(int num, int rad)
{
    char digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
    int i;
    char buf[66];   /* enough space for any 64-bit in base 2 */
    buf[0]=0;

    /* bounds check for radix */
    if (rad < 2 || rad > 62)
        return NULL;
    /* if num is zero */
    if (!num)
        return StrDup("0");
        //return strdup("0");

    /* null terminate buf, and set i at end */
    buf[65] = '\0';
    i = 65;

    if (num > 0) {  /* if positive... */
        while (num) { /* until num is 0... */
            /* go left 1 digit, divide by radix, and set digit to remainder */
            buf[--i] = digits[num % rad];
            num /= rad;
        }
    } else {    /* same for negative, but negate the modulus and prefix a '-' */
        while (num) {
            buf[--i] = digits[-(num % rad)];
            num /= rad;
        }
        buf[--i] = '-';
    }   
    /* return a duplicate of the used portion of buf */
    return StrDup(buf + i);
    //return strdup(buf + i);
}

void delaySome(void) //test the timer
{
    time_t x = clock();
    while ((clock() - x)<1000); 
}
ryyker
  • 22,849
  • 3
  • 43
  • 87
  • the compiler I have to use doesn't support that method and I have to do it out manually – flylib Sep 28 '13 at 23:04
  • Okay, then I do not have a direct answer. But ***[here](http://stackoverflow.com/a/18879257/645128)*** is some code that does conversions from string to float to integer to binary. (Just cut and past the code to see how it works.) The bit manipulations may provide some small piece to what you are trying. Good luck. – ryyker Sep 28 '13 at 23:21
  • Any radix conversion..., This is not c, ***[its c++ example](http://www.daniweb.com/software-development/cpp/code/217255/converting-from-any-radix-to-another)***, and ***[here too](http://www.cplusplus.com/articles/1wTbqMoL/)*** but they may give you some ideas... – ryyker Sep 28 '13 at 23:25
  • My mistake compiler does work but that's converting any Radix Base Number (between 2 and 36) to Decimal and I need to do the opposite by converting Decimal to Any Radix Base Number so an example would be decimal 130 radix 12 = AA – flylib Sep 28 '13 at 23:25
  • This looks like it will ***[convert from base 10 to another radix](http://dystopiancode.blogspot.com/2011/10/radix-conversion-in-c.html)***. (You have to fix the malloc, needs one more byte) – ryyker Sep 28 '13 at 23:43
  • Using `strdupa()` in place of `strdup()` in my implementation would actually be a bad thing, since the memory it allocates wouldn't remain valid when the function returned. A better option would be to just copy the result to a user-supplied buffer instead. – Dmitri Sep 29 '13 at 22:50
  • ***Agreed.*** - The GNU `strdupa()` will allocate memory onto the stack, then free it when you leave the function that called it. A passed in pointer would work better, and be faster too. You could simply return buf without duplicating it. – ryyker Sep 29 '13 at 23:20
2

Try this.

#include <stdio.h>
#include <string.h>

static void decimal_to_radix_base(char *s,int x,int base,int maxlen);
static void insert_char(char *s,int c,int maxlen);
static int digit(int x);


void main(void)

    {
    int x,base;
    char s[128];

    x=130;
    base=12;
    decimal_to_radix_base(s,x,base,127);
    printf("%d base %d = %s\n",x,base,s);
    }


static void decimal_to_radix_base(char *s,int x,int base,int maxlen)

    {
    int sign;

    sign= (x<0) ? -1 : 1;
    if (x<0)
        x=-x;
    s[0]='\0';
    if (x==0)
        {
        if (maxlen > 0)
            strcpy(s,"0");
        return;
        }
    while (x>0)
        {
        int r;
        r=x%base;
        insert_char(s,digit(r),maxlen);
        x /= base;
        }
    if (sign<0)
        insert_char(s,'-',maxlen);
    }


static void insert_char(char *s,int c,int maxlen)

    {
    int len;

    len=strlen(s);
    if (len>=maxlen)
        memmove(&s[1],s,len-1);
    else
        memmove(&s[1],s,len+1);
    s[0]=c;
    }


static int digit(int x)

    {
    if (x<10)
        return('0'+x);
    return('A'+x-10);
    }
willus
  • 501
  • 3
  • 7
  • thanks the code works and gives correct answers – flylib Sep 29 '13 at 01:25
  • This only works for bases up to 16, does a lot of unnecessary `memmove`s, and doesn't work for certain numbers (such as -2147483648 with 32-bit 2's complement -- since on 2's complement machines the most negative representable numbers have larger magnitudes than the most positive). – Dmitri Sep 29 '13 at 02:12
  • Why do you say it only works on bases up to 16? Should be fine up to base 36. Small memmoves are very inexpensive on modern CPUs. And I wasn't especially worried about extreme cases, though I think it fails on a trivial case: zero. – willus Sep 29 '13 at 05:46
  • Sorry, I see it would work past base 16 - I was mistaken on that point. However, you're still performing a `strlen()` and a `memmove()` on the result so far for *each new digit* of the resulting string - even if the overhead is small for each move, it adds up somewhat for longer numbers, and that overhead is easily avoided with a different implementation. – Dmitri Sep 29 '13 at 06:13
  • Dmitri, yes, I like your implementation better. I'd comment on it, but I can't yet. Why did you put 62 chars in your "digits" string but then return a NULL if radix > 36? – willus Sep 29 '13 at 12:00
  • Added check for zero. – willus Sep 29 '13 at 12:10
  • 1
    Interesting: I tested Dmitri's solution vs. mine on the first 50 million integers in base 20 (modern Intel CPU, Win 7, MinGW gcc 4.7.3 -Ofast). Mine ran faster (3.4 vs. 3.7 seconds) because the call to strdup is apparently more expensive than all my memmov's for my O/S and compiler. If I remove the call to strdup and just return a pointer to buf+i (making buf static) in Dmitri's version, it runs much faster: 0.8 seconds! In Linux, slightly different results (different CPU): My version: 4.4 s, Dmitri's: 2.8 s, Dmitri's w/o strdup: 1.2 s. – willus Sep 29 '13 at 15:03
  • A little comparative analysis every now and then is not a bad thing. – ryyker Sep 29 '13 at 16:07
  • The Java code shown in the question before it was edited had 62 chars in its baseDigits string so I did the same. You can change the upper limit on the radix check to 62... I think I just used 36 sort of by mistake since the 2..36 range was mentioned in several comments. – Dmitri Sep 29 '13 at 17:34
  • @willus - Just curious, what happened with `strdup()` in Dmitri's code after 50,000,000 integers? Did you track the memory usage? – ryyker Sep 30 '13 at 17:09
  • @ryyker `strdup()` doesn't automatically leak memory... it just leaves the programmer responsible for freeing it. If the returned strings are `free()`d after use, there's no leak. It'd still be faster to use a user-supplied buffer and avoid the repeated allocations and deallocations, though (the need to free the strings and the suggestion to modify the function to use a user-provided buffer were both mentioned in my answer, btw). – Dmitri Oct 01 '13 at 02:10
  • @ryyker - I tried it with and without calling free() each time. Didn't make much difference to the run time, and I have enough memory that I didn't run out. – willus Oct 01 '13 at 05:36