0

From what I understand, mpz_t are nothing but a single-item array.

According to gmp.h:

{
  short int _mp_alloc;      /* Number of *limbs* allocated and pointed
                   to by the D field.  */
  short int _mp_size;       /* abs(SIZE) is the number of limbs
                   the last field points to.  If SIZE
                   is negative this is a negative
                   number.  */
  mp_limb_t *_mp_d;     /* Pointer to the limbs.  */
} __mpz_struct;

typedef __mpz_struct mpz_t[1];

That means that, given the way C handles arrays, mpz_t is effectively a pointer itself (to the first element of the one-element array).

Now, let's say I create a new mpz_t, within a function, "box" the pointer in an unsigned long long and return, then... chaos. (Since, I guess, the pointer is no longer valid?)

The only way I could in theory make it work is by moving the mpz_t declaration outside the function body (basically, make it a global)

Any other - more sensible - ideas?


Minimal example:

typedef uint64_t Value;

#define addWillOverflow(x,y,z) __builtin_sadd_overflow(x,y,z)

#define BITFIELD 56

#define MASK(x) ((Value)(x) << BITFIELD)
#define UNMASK  0x00FFFFFFFFFFFFFF

#define toI(v)  ( (Value)(v & 0xFFFFFFFFu) | MASK(IV) )
#define toG(v)  ( (Value)(v) | MASK(GV) )
#define GG(v)   ( (v & UNMASK) )

Value addNumbers(int a, int b) {
    Int32 res;
    if (addWillOverflow(a,b,&res)) {
        printf("overflow\n");
        mpz_t ret;
        mpz_init_set_si(ret,a);
        mpz_add_ui(ret,ret,b);
        return toG(ret);
    }
    else {
        printf("normal addition\n");
        return toI(res);
    }
}

int main(int argc, char** argv) {

    printf("3 + 5 = ");
    printLnValue(addNumbers(1,2));

    printf("2b + 2b = ");
    Value a = addNumbers(2000000000,2000000000);
    printLnValue(a);
    printf("pointer: %p\n",GG(a));

    Value b = addNumbers(2100000000,2011111111);
    printLnValue(b);
    printf("pointer: %p\n",GG(b));
}

Output:

3 + 5 = normal addition
3
2b + 2b = overflow
4000000000
pointer: 0x7ffee5b95630
overflow
4111111111
pointer: 0x7ffee5b95630

As you can see, the returned pointer is... the same. Although I would expect a different one.


UPDATE no 2

I'm currently thinking of doing it like this: (I'll see how it goes and then post it as a solution myself)

    mpz_t* ret = malloc(sizeof(mpz_t));
    mpz_init_set_si(*ret,a);
    mpz_add_ui(*ret,*ret,b);
    return toG(ret);
Dr.Kameleon
  • 22,532
  • 20
  • 115
  • 223
  • Show us your actual code. – John Zwinck Jan 23 '20 at 12:21
  • @JohnZwinck I'll try to make a minimal example. – Dr.Kameleon Jan 23 '20 at 12:24
  • @JohnZwinck I just added some example code with its output to see what my issue is. – Dr.Kameleon Jan 23 '20 at 12:39
  • I think you just need to work with the restrictions that `mpz_t` values are passed by reference and cannot be used as the return value of a function (which would not compile anyway). – Ian Abbott Jan 23 '20 at 13:39
  • 2
    Why would you expect a different address? `ret` is clearly a local variable (i.e. stack-allocated). Anyway, the `malloc` solution is correct. You might be interested by the note in [this answer](https://stackoverflow.com/a/32533616/1566221). – rici Jan 23 '20 at 14:50
  • 1
    Your goal is to return an mpz_t, just look at how GMP does it (mpz_add for instance). Or move to C++, use mpz_class like you would use int, and worry about something else. – Marc Glisse Jan 23 '20 at 18:19

0 Answers0