rkeller
rkeller

Reputation: 69

Convert an number with arbitrary length from decimal to hex in c++

I've seen several examples for converting a number from decimal to hex (or base 10 to base 16), but I have a few restrictions for what I'm trying to do. I need to be able to convert a string with a decimal number into another string as a hex number. The number is potentially too big to fit inside of any primitive data types - ie: can't use ints, unsigned ints, doubles, etc...

This should be able to perform the same calculations as listed on this page. http://www.kaagaard.dk/service/convert.htm

I've tried this, but it hasn't worked for me.

The call to the function:

const int maxLen = 256;
char destination[maxLen];
int retVal = convertBase(destination, maxLen, "123487032174829820348320429437483266812812");

The function definition:

int convertBase(char* dest, int maxDestLength, const char* inputInBase10)
{
    const char lookUpTable[] = { "0123456789abcdef" };
    const std::string input = inputInBase10;
    const unsigned int inputSize = input.length();

    std::string output;
    output.reserve(2 * inputSize);

    for(unsigned int i = 0; i < inputSize; ++i)
    {
        const unsigned char c = input[i];
        output.push_back(lookUpTable[c >> 4]);  
        output.push_back(lookUpTable[c & 15]);
    }

    if(output.length() < maxDestLength)
        strcpy_s(dest, output.length(), output.c_str());
    else
        strcpy_s(dest, maxDestLength, output.c_str());

    cout << dest << endl;

    return strlen(dest);
}

The expected hex number: "16ae5514d07e120126dfbcb3073fddb2b8c"

The actual hex number generated: "313233343837303332313734383239383230333438333230343239343337343833323636383132383132"

Also, I keep getting an error that the buffer is too small when passing back into a char* (repeated below)

    if(output.length() < maxDestLength)
        strcpy_s(dest, output.length(), output.c_str());
    else
        strcpy_s(dest, maxDestLength, output.c_str());

Upvotes: 2

Views: 2354

Answers (2)

paddy
paddy

Reputation: 63471

So if you're in a hurry to get anything, and are not too concerned about efficiency, try this. I've adapted a function that I wrote for the following SO question: c++ string (int) + string (int)

First, I modified the Add function to work for any base between 2 and 36:

const int MIN_BASE = 2;
const int MAX_BASE = 36;

static bool tablesInitialised = false;
static char tblIntToChar[MAX_BASE] = {0};
static int tblCharToInt[256] = {0};

void InitTables()
{
    if( tablesInitialised ) return;

    for( int i = 0; i < 10; i++ ) {
        tblIntToChar[i] = '0' + i;
        tblCharToInt[tblIntToChar[i]] = i;
    }

    for( int i = 0; i < 26; i++ ) {
        tblIntToChar[i+10] = 'a' + i;
        tblCharToInt['a' + i] = i + 10;
        tblCharToInt['A' + i] = i + 10;
    }

    tablesInitialised = true;
}


// Adds two numbers using long addition.
string Add( const string& a, const string& b, int base=10 )
{
    InitTables();
    if( base > MAX_BASE || base < MIN_BASE ) return "";

    // Reserve storage for the result.
    string result;
    result.reserve( 1 + std::max(a.size(), b.size()) );

    // Column positions and carry flag.
    int apos = a.size();
    int bpos = b.size();
    int carry = 0;

    // Do long arithmetic.
    while( carry > 0 || apos > 0 || bpos > 0 )
    {
        if( apos > 0 ) carry += tblCharToInt[(unsigned char)a[--apos]];
        if( bpos > 0 ) carry += tblCharToInt[(unsigned char)b[--bpos]];
        result.push_back(tblIntToChar[carry%base]);
        carry /= base;
    }

    // The result string is backwards.  Reverse and return it.
    reverse( result.begin(), result.end() );
    return result;
}


// Converts a single value to some base, intended for single-digit conversions.
string AsBase( int number, int base )
{
    InitTables();
    if( number <= 0 ) return "0";
    string result;
    while( number > 0 ) {
        result += tblIntToChar[number%base];
        number /= base;
    }
    reverse( result.begin(), result.end() );
    return result;
}

And, using all the above, here is a function to convert from one arbitrary base to another:

// Converts a number from one base to another.
string ConvertBase( const string & number, int oldBase, int newBase )
{
    InitTables();
    string result;

    for( unsigned digit = 0; digit < number.size(); digit++ )
    {
        int value = tblCharToInt[(unsigned char)number[digit]];
        if( result.empty() )
        {
            result = AsBase( value, newBase );
        }
        else 
        {
            string temp = result;
            for( int i = 1; i < oldBase; i++ ) {
                temp = Add( result, temp, newBase );
            }
            result = Add( temp, AsBase(value, newBase), newBase );
        }
    }

    return result;
}

This is just something I hacked up using code I mostly had kicking around in my StackOverflow 'for fun' sandbox. It's pretty clunky the way it performs oldBase additions instead of just doing one multiplication. This would be a lot better if you made a Multiply function to do long multiplication.

Upvotes: 3

Lee Irvine
Lee Irvine

Reputation: 3347

You can't solve the problem by going through each character individually. The reason is that two characters need to be considered when deciding which hex digit needs to be used.

for example "12" would be correctly represented as "C" where your algorithm won't take that into consideration.

You should also be reading from right to left rather than left to right

Upvotes: 1

Related Questions