Recursive function to convert between number bases fails at certain numbers
Asked Answered
C

4

1

I'm trying to a create an algorithm that can convert base 10 numbers into base n numbers, where n is at most 10. However, for some weird reason the following algorithm in C fails at certain critical points for each base. For example, for base 2 and base 3 conversions, all numbers up to and including 1023 and 52,487 work, respectively, but numbers beyond that produce some weird negative result. I can't figure out why this is happening; can anyone help me?

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

int returnint;

int baseconvert(int number,int base) {
    if(number == 0 || base == 10) {
        return returnint;
    }
    returnint = (number % base) + (10 * baseconvert(number / base, base));
    return returnint;
}

int main() {
    fprintf(stdout,"%d\n",baseconvert(1023,2));
    fprintf(stdout,"%d\n",baseconvert(52487,3));
}

EDIT:

Here is the printed result of the above print statements, if that's helpful:

1410065408
-2094967296
Crispation answered 23/7, 2014 at 17:4 Comment(6)
baseconvert(1023,2) does work in gcc and baseconvert(52487,3) is most likely an overflow.Mcneely
Not the answer just a note: you're missing a return in your main() function.Ventriloquize
@Mcneely Yes baseconvert(1023,2) and baseconvert(52487,3) work; those are posted as examples of the highest values that work. I tried using a larger integer, as I commented below on Mark Wilkin's post, but that didn't work.Crispation
The main problem here is that you're taking integers as if they are dedicated for a specific base. Integers are for representing counts of things that extend to the negative range as well. The value you are to hold in an int variable is the count, for example, the amount of apples in a bag. Regardless of how you represent it in different bases, there are fixed amount of apples in that bag, and that fixed amount is to be held within the int. Base should only matter while outputting the value. For example: int n = 20; printf( "%d %x %o", n, n, n ); will output n as 20, 14 and 24.Choirboy
the code has a bug. if the base is 10, then the code returns 'returnint' however, the var returnint has not been set so the function will return garbage.Epicurus
the code does not seem to work as the base 2 of 1023 is 1111111111, not 1410065408Epicurus
I
0

It appears that the integer value is overflowing. For example, the decimal value 1023 in base two is 1111111111. If it is a 4 byte integer, then it will overflow when trying to "add" one more digit (the max signed integer in this case being 2147483647).

Since it appears your goal is to display a number in a different base, it might make more sense to store the digits in a character array.

Impiety answered 23/7, 2014 at 17:10 Comment(2)
Hmm, even if I increase the size of returnint to unsigned long int (and put it inside the body of the function baseconvert as lgor suggested), the value still overflows even though the integer can now be 32 bits in size. Perhaps if I change over to appending digits to a string... I'll be right back with the result of that.Crispation
Depending on the platform, a long may still only be 4 bytes rather than 8, so that may not help. And I see that I suggested the character array at the same time you did. :)Impiety
S
1

Your algorithm is very limited in the range of numbers vs. bases. The smaller the base is the more digits are needed to represent it. And since you store the result in decimal form, you'll waste your available data range very quickly. There is no fundamental data type that can hold the results for all the possible inputs. E.g., maximal 31-bit decimal number (normal integer, dropping the sign bit) will result in 31-digit output!

You have several options to cope with this:

  • Allocate a stack large enough and push the digits into it. Upon completion, print the stack contents.
  • Print the digits immediately without saving, this will eliminate the need to allocate anything. E.g.:

#include <stdio.h>

void baseconvert(int number,int base) 
{
    if(number > 0) 
    {
        int digit = (number % base);
        baseconvert(number / base, base);
        printf("%d",digit);
    }
    else
    {
        printf("\n");
    }
}

int main() 
{
    baseconvert(1023,2);
    baseconvert(52487,3);
}
Salve answered 23/7, 2014 at 18:14 Comment(0)
I
0

It appears that the integer value is overflowing. For example, the decimal value 1023 in base two is 1111111111. If it is a 4 byte integer, then it will overflow when trying to "add" one more digit (the max signed integer in this case being 2147483647).

Since it appears your goal is to display a number in a different base, it might make more sense to store the digits in a character array.

Impiety answered 23/7, 2014 at 17:10 Comment(2)
Hmm, even if I increase the size of returnint to unsigned long int (and put it inside the body of the function baseconvert as lgor suggested), the value still overflows even though the integer can now be 32 bits in size. Perhaps if I change over to appending digits to a string... I'll be right back with the result of that.Crispation
Depending on the platform, a long may still only be 4 bytes rather than 8, so that may not help. And I see that I suggested the character array at the same time you did. :)Impiety
P
0

On all processors that I know of, integers are already stored in binary form; this implies base 2. This value can be displayed in whatever base you desire, with some work on your part. printf() and friends allow you to easily print in base 10 (%d) and base 16 (%x). It is not difficult to imagine a method that would convert a binary (base 2) integer value into a character representation in base n.

I seriously doubt that you really intend to change the actual value of the integer, as you are doing. Like @ThoAppelsin said above, the number of apples in the bag stays the same, regardless of which base you choose for display.

By simply creating a method that represents (with digits) the integer in any base, you will also solve your overflow problem!

Pyo answered 23/7, 2014 at 18:26 Comment(0)
E
0

Your result is overflowing the integer range. Try using strings instead. This is the pseudocode which relies on strings to represent numbers and it can convert numbers from any base to any other base between 2 and 36 inclusive (using digits and upper case letters):

function ConvertNumber(number, b, d)
begin
    newNumber = ""
    while number <> "0"
        begin
            number = Divide(number, b, d, out remainder)
            newDigit = ValueToDigit(remainder)
            newNumber = Concatenate(newDigit, newNumber)
        end
    if newNumber ="" then
        newNumber = "0"
end

function Divide(number, base, divisor, out remainder)
begin
    remainder = 0
    result = ""
    for i = 0 to Length(number) - 1
        begin
            digitValue = DigitToValue(number[i])
            remainder = base * remainder + digitValue
            newDigitValue = remainder / divisor -- integer division
            remainder = remainder mod divisor
            if newDigitValue > 0 OR result <> "" then
                newDigit = ValueToDigit(newDigitValue)
                result = Concatenate(result, newDigit)
        end
    if result = "" then
        result = "0"
    return result
end

You can find the whole math and implementation in this article: Converting Number Bases.

Extrados answered 17/6, 2015 at 12:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.