Search code examples
cmemorymallocdynamic-memory-allocationgmp

Using Malloc to handle large numbers with GMP


Solved:

I feel so stupid. GMP is fine and it was my oversight. After using size_t mpz_sizeinbase (const mpz_t op, int base), I realised my char array I used to copy the result into was too small. Increasing it size solved it. Thanks for the help!


My task is to write a C program which calculates the elements of the Fibonacci Sequence from the 1024th element to the 1048576th element (from the 10th power of 2 to the 20th power of 2, increasing by the power of 2). For this I'm using GMP library to handle the numbers. The problem is, that around the 17th power of 2, the number is so big, that even GMP can't handle it, which means I should use malloc().

Here is my main() (I modified the pasted code by taking out unnecessary parts like writing to file and time measurements which will be used for another part of the program):

int main(){
    int powersOfTwo[11];
    char res[10000];
    char *c;
    c = res;

    for(int i = 0; i < 11; i++){
        powersOfTwo[i] = normalPower(2,i+10);
    }
    for(int i = 0; i < 11; i++){
        fibo(c, powersOfTwo[i]);
        printf("The %d th element of Fibonacci is %s\n",powersOfTwo[i],res);
        memset(res, 0, sizeof res);
    }

    return 0;
} 

Now here is the simple normalPower function (Doesn't really have anything to do with the problem just for the sake of clarity):

int normalPower(int n1, int n2){
    if(n2 == 0){
        return 1;
    }else{
        int temp = n1;
        for(int i = 1; i < n2; i++){
            temp *= n1;
        }
        return temp;
    }
}

And now the problem, the fibo function:

void fibo(char *c, int n){
    mpz_t *fib1;
    mpz_t *fib2;
    mpz_t *temp;

    fib1 = (mpz_t *) malloc(101000000 * sizeof(mpz_t));
    fib2 = (mpz_t *) malloc(101000000 * sizeof(mpz_t));
    temp = (mpz_t *) malloc(101000000 * sizeof(mpz_t));

    if (NULL == fib1 || NULL == fib2 || NULL == temp){
      printf("ERROR: Out of memory\n");
    }
    mpz_init(*fib1);
    mpz_init(*fib2);
    mpz_init(*temp);

    mpz_set_str(*fib1,"0",10);
    mpz_set_str(*fib2,"1",10);

    int i;
    if(n == 0){
      char *d = mpz_get_str(NULL,10,*fib1);
      strcpy(c,d);
    }

    if(n == 1){
      char *d = mpz_get_str(NULL,10,*fib2);
      strcpy(c,d);
    }

    if(n > 1){
      for(i = 1; i < n; i++){
          mpz_set(*temp, *fib2);
          mpz_add(*fib2, *fib1, *fib2);
          mpz_set(*fib1,*temp);

      }
      char *d = mpz_get_str(NULL,10,*fib2);
      strcpy(c,d);
    }

    free(fib1);
    free(fib2);
    free(temp); 
}

Originally I used simply mpz_t-s, initing them and mpz_clear()-ing them in the end, no pointers and malloc(), but that led to Segmentation fault (core dumped) error after calculating the 2 on the power of 17(-ish) element. This was a solution I found on the Internet and this was almost the biggest number I could allocate, still nothing changes, the program stops at the same point with the same error message. I also tried to use mp_set_memory_functions() and creating a custom mallocWrapper() and giving it the GMP, but that didn't seem to work either. Of course I'm 99% sure, it's because I'm new to GMP and relative new to using malloc() so my code is probably making most of you tearing your hair out right now and I apologise for that.

So basically my question is: How should I use malloc() to get enough memory for the numbers?

Thanks for all the help in advance!


Solution

  • These lines:

    mpz_t *fib1;
    mpz_t *fib2;
    mpz_t *temp;
    
    fib1 = (mpz_t *) malloc(101000000 * sizeof(mpz_t));
    fib2 = (mpz_t *) malloc(101000000 * sizeof(mpz_t));
    temp = (mpz_t *) malloc(101000000 * sizeof(mpz_t));
    

    look very wrong to me (and your entire source code smells very bad, you should drop it). It seems that you want to allocate 101000000 different bignums, and I don't understand why you need that much.

    Read carefully the documentation of GMPlib and the definition of Fibonnaci numbers. You only need a few mpz_t (very probably you need less than half a dozen of mpz_t variables), not many millions of them. Think about the mathematic definition of Fibonacci before coding your program. Notice that if you know Fib(10000) and Fib(10001) computing Fib(10002) is easy with GMPlib and then you don't need Fib(10000) any more and you can print Fib(10002) as soon as it has been computed. Be aware that you can use mpz_set to assign GMP big integers.

    (you really should start rewriting your program from scratch after having thinking on the math; you can dump your existing code)

    Don't forget to initialize every variable, to call mpz_init appropriately.

    Compile with all warnings and debug info (gcc -Wall -Wextra -g). Use the debugger (gdb) and valgrind and perhaps -fsanitize= debugging options

    As far as I understand you don't need even a single explicit call to malloc in your code (of course, internally GMPlib is using malloc).

    (BTW, as a student it is helpful to assume that your exercise is doable quite easily)