Skip to main content
edited tags
Source Link
200_success
  • 144.1k
  • 22
  • 188
  • 473

I needwrote a function to convert an unsigned integer number given by a string from one base to another. I wrote the following code. Can Can you please give me any tips to improve it? Thanks :)

I need a function to convert an unsigned integer number given by a string from one base to another. I wrote the following code. Can you please give me any tips to improve it? Thanks :)

I wrote a function to convert an unsigned integer number given by a string from one base to another. Can you please give me any tips to improve it?

Source Link
eanmos
  • 405
  • 4
  • 21

Convert a number from one base to another

I need a function to convert an unsigned integer number given by a string from one base to another. I wrote the following code. Can you please give me any tips to improve it? Thanks :)

#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>

#define MAX_BASE() strlen(alphabet)
static char const *alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";

static unsigned int ullong2str(char [], unsigned long long, unsigned int);
static unsigned long long str2ullong(char const [], unsigned int);
static char *convertToNewBase(char const [], unsigned int, unsigned int);
static long indexOf(char const [], char);
static unsigned long long power(unsigned int, unsigned int);
static void reverse(char []);

int main(void)
{
    char *decimal = "28697";
    char *binary = convertToNewBase(decimal, 10, 2);
    
    printf("%s (base 10) = %s (base 2)\n", decimal, binary);
    perror(NULL);

    free(binary);
}

/*
===============
char *convertToNewBase(char [], unsigned int, unsigned int);

The function converts an unsigned integer number from the source base to
destination base. The function allocates a buffer of length large enough
to store the result and returns it. If an error occurs the function
returns NULL.
===============
*/
char *convertToNewBase(char const s[], unsigned int src, unsigned int dest)
{
    assert(s);
    assert(src >= 2 && src <= MAX_BASE());
    assert(dest >= 2 && dest <= MAX_BASE());

    errno = 0;

    unsigned long long const n = str2ullong(s, src);

    if (errno != 0)
        return NULL;
    
    unsigned int const resultLength = ullong2str(NULL, n, dest);
    char *result = calloc(resultLength + 1, 1);
    if (result == NULL)
        return NULL;

    ullong2str(result, n, dest);

    return result;
}

/*
===============
unsigned long long ullong2str(char [], unsigned long long, unsigned int);

The function converts an unsigned integer value to a null-terminated string
using the specified base and stores the result in the array given by the first
parameter. It assumes that size of the array is large enough to store the
result.

The function returns length of the result string. If the first parameter is
NULL nothing is written, however the return value is still calculated and
returned.
===============
*/
unsigned int ullong2str(char out[], unsigned long long n, unsigned int base)
{
    assert(base >= 2 && base <= MAX_BASE());

    unsigned int i = 0;

    do {
        if (out != NULL)
            out[i] = alphabet[n % base];
    
        n /= base;
        i++;
    } while (n);
    
    if (out != NULL) {
        out[i] = '\0';
        reverse(out);
    }
    
    return i;
}

/*
===============
unsigned long long str2ullong(char const [], base_t base);

If no errors occurs the function returns an unsigned integer value
corresponding to the contents of the input string. If the result is
too large the function returns 0 and sets errno to ERANGE [1]. If the
arguments is invalid the function return 0 and sets errno to EINVAL [2].

1. str2llong("18446744073709551616", 10) in most cases returns 0 and sets
   errno to ERANGE, since 18446744073709551616 is greater than ULLONG_MAX
   (in most cases).

2. str2llong("10191", 2) returns 0 and sets errno to EINVAL, since '9' is
   not allowed in a binary number.
===============
*/
unsigned long long str2ullong(char const s[], unsigned int base)
{
    assert(s != NULL);
    assert(base >= 2 && base <= MAX_BASE());

    unsigned long long r = 0;

    for (unsigned int i = strlen(s), j = 0; i-- > 0; j++) {
        long const index = indexOf(alphabet, s[i]);
        if (index == -1 || index >= (int) base) {
            errno = EINVAL;
            return 0;
        }

        unsigned long long const t = (unsigned long long) index * power(base, j);
        if (r > ULLONG_MAX - t) {
            errno = ERANGE;
            return 0;
        }

        r += t;
    }

    return r;
}

/*
===============
unsigned long long power(unsigned int, unsigned int);

If no errors occurs the function returns aⁿ. If the result is too large the
function returns 0 and sets errno to ERANGE.
===============
*/
unsigned long long power(unsigned int a, unsigned int n)
{
    unsigned long long r = 1;

    while (n--) {
        /* If a * r would overflow… */
        if (a > ULLONG_MAX / r) {
            errno = ERANGE;
            return 0;
        }

        r *= a;
    }

    return r;
}

/*
===============
long indexOf(char const [], char);

The function return index of the first occurence of the character in the
string. If the character is not found returns -1.
===============
*/
long indexOf(char const s[], char c)
{
    assert(s);

    for (size_t i = 0; s[i]; i++)
        if (s[i] == c)
            return (long) i;

    return -1;
}

/*
===============
void reverse(char []);

The function reverses the input string.
===============
*/
void reverse(char s[])
{
    assert(s);
    assert(strlen(s));

    for (unsigned int i = 0, j = strlen(s) - 1; i < j; i++, j--) {
        char const tmp = s[i];
        s[i] = s[j];
        s[j] = tmp;
    }
}