1

I am stuck with a problem. I am working on a hardware which only does support 32 bit operations.

sizeof(int64_t) is 4. Sizeof(int) is 4.  

and I am porting an application which assumes size of int64_t to be 8 bytes. The problem is it has this macro BIG_MULL(a,b) ( (int64_t)(a) * (int64_t)(b) >> 23)

The result is always a 32 bit integer but since my system doesn't support 64 bit operation, it always return me the LSB of the operation, rounding of all the results making my system crash.

Can someone help me out?

Regards, Vikas Gupta

Viks
  • 29
  • 1
  • 3
  • 8
    Wait... it defines `int64_t` as something that's *not* 64 bits? Does this compiler/libc claim C99 conformance? – Ignacio Vazquez-Abrams Apr 18 '10 at 04:46
  • Correct me if I'm wrong - you're trying to multiply with this macro, right? So, you're saying you want to do some mathematical operations on numbers - what operations exactly? – Poni Apr 18 '10 at 04:54
  • Yeah I can change the macro. All the multiplication is done by using this macro. Since the hardware doesn't have any 64 bit manipulation support, I need to modify it. – Viks Apr 18 '10 at 04:59
  • Are you sure your macro says >>23 and not >>32 – Romain Hippeau Apr 18 '10 at 05:13
  • what type are `a` and `b` before being cast to int64_t? If they're 4-byte ints, and if that `>>23` is actually supposed to be `>>32`, then this is easy! `#define BIG_MULL(a,b) ( ((a)>>16) * ((b)>>16))` – Ponkadoodle Apr 18 '10 at 05:22
  • @Romain:-it is 23 bit not 16 bit, as it is fixed point calculation. @Wallacoloo:- A and B are of int type which is 4 32 bit. – Viks Apr 18 '10 at 05:54
  • What compiler is this, and are you sure you can't upgrade it? – Potatoswatter Apr 18 '10 at 09:00

5 Answers5

7

You simply cannot reliably store 64 bits of data in a 32-bit integer. You either have to redesign the software to work with 32-bit integers as the maximum size available or provide a way of providing 64 bits of storage for the 64-bit integers. Neither is simple - to be polite about it.

One possibility - not an easy one - is to create a structure:

typedef struct { uint32_t msw; uint32_t lsw; } INT64_t;

You can then store the data in the two 32-bit integers, and do arithmetic with components of the structure. Of course, in general, a 32-bit by 32-bit multiply produces a 64-bit answer; to do full multiplication without overflowing, you may be forced to store 4 16-bit unsigned numbers (because 16-bit numbers can be multiplied to give 32-bit results w/o overflowing). You will use functions to do the hard work - so the macro becomes a call to a function that accepts two (pointers to?) the INT64_t structure and returns one.

It won't be as fast as before...but it has some chance of working if they used the macros everywhere that was necessary.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • Yes, I tried using the 16 bit manipulations but it really doesn't work. Since the operations are all on signed arithmetic, will it make any difference. – Viks Apr 18 '10 at 05:18
  • @Viks: you can always determine qhat the sign of the result should be ahead of time then do your arithmetic on unsigned values 16 bits at a time. It's just like having the computer do arithmetic like you learned in grade school. – Michael Burr Apr 18 '10 at 05:21
  • 1
    @Viks: you have to think hard about it, but it is doable. You have to deal with signs etc yourself. As I said "possibility - not an easy one"...There must be packages around for 64-bit integer arithmetic on 32-bit machines because C99 compilers have to support 'long long' with a minimum of 64-bits (as well as 'unsigned long long'), and therefore, they need such packages. Note that the unsigned and signed arithmetic is slightly different; there's a lot in common and some crucial differences. – Jonathan Leffler Apr 18 '10 at 05:40
3

I assume that the numbers that you are trying to multiply together are 32-bit integers. You just want to generate a product that may be larger than 32 bits. You then want to drop some known number of least significant bits from the product.

As a start, this will multiply the two integers together and overflow.

#define WORD_MASK ((1<<16) - 1)
#define LOW_WORD(x)  (x & WORD_MASK) 
#define HIGH_WORD(x) ((x & (WORD_MASK<<16)) >> 16)
#define BIG_MULL(a, b) \
    ((LOW_WORD(a)  * LOW_WORD(b))  <<  0) + \
    ((LOW_WORD(a)  * HIGH_WORD(b)) << 16) + \
    ((HIGH_WORD(a) * LOW_WORD(b))  << 16) + \
    ((HIGH_WORD(a) * HIGH_WORD(b)) << 32)

If you want to drop the 23 least-significant bits from this, you could adjust it like so.

#define WORD_MASK ((1<<16) - 1)
#define LOW_WORD(x)  (x & WORD_MASK) 
#define HIGH_WORD(x) ((x & (WORD_MASK<<16)) >> 16)
#define BIG_MULL(a, b) \
    ((LOW_WORD(a)  * HIGH_WORD(b)) >> 7) + \
    ((HIGH_WORD(a) * LOW_WORD(b))  >> 7) + \
    ((HIGH_WORD(a) * HIGH_WORD(b)) << 9)

Note that this will still overflow if the actual product of the multiplication is greater than 41 (=64-23) bits.


Update:

I have adjusted the code to handle signed integers.

#define LOW_WORD(x)  (((x) << 16) >> 16) 
#define HIGH_WORD(x) ((x) >> 16)
#define ABS(x) (((x) >= 0) ? (x) : -(x))
#define SIGN(x) (((x) >= 0) ? 1 : -1)
#define UNSIGNED_BIG_MULT(a, b) \
    (((LOW_WORD((a))  * HIGH_WORD((b))) >> 7) + \
     ((HIGH_WORD((a)) * LOW_WORD((b)))  >> 7) + \
     ((HIGH_WORD((a)) * HIGH_WORD((b))) << 9))
#define BIG_MULT(a, b) \
    (UNSIGNED_BIG_MULT(ABS((a)), ABS((b))) * \
     SIGN((a)) * \
     SIGN((b)))
Matthew T. Staebler
  • 4,756
  • 19
  • 21
0

If you change your macro to

#define  BIG_MULL(a,b) ( (int64_t)(a) * (int64_t)(b))

since it looks like int64_t is defined for you it should work

Romain Hippeau
  • 24,113
  • 5
  • 60
  • 79
0

While there are other questions raised by sizeof(int64_t) == 4, this is wrong:

#define BIG_MULL(a,b) ( (int64_t)(a) * (int64_t)(b) >> 23)

The standard requires intN_t types for values of N = 8, 16, 32, and 64... if the platform supports them.

The type you should use is intmax_t, which is defined to be the largest integral type the platform supports. If your platform doesn't have 64-bit integers, your code won't break with intmax_t.

Chris Lutz
  • 73,191
  • 16
  • 130
  • 183
  • these macros are user defined and is not supported by compiler. The system doesn't break as int64_t is defined to be of the largest supported integral type which is 4 byte. – Viks Apr 18 '10 at 05:15
  • if `intmax_t` is 4 bytes, and `int64_t` is 4 bytes, why would code break under one, but not the other? They sound exactly the same to me. – Ponkadoodle Apr 18 '10 at 05:18
0

You might want to look at a bignum library such as GNU GMP. In one sense a bignum library is overkill, since they typically support arbitrary sized numbers, not just a increased in fixed size numbers. However, since it's already done, the fact that it does more than you want might not be an issue.

The alternative is to pack a couple 32-bit ints into a struct similar to Microsoft's LARGE_INTEGER:

typedef union _LARGE_INTEGER {
    struct {
        DWORD LowPart;
        LONG HighPart;
    };
    struct {
        DWORD LowPart;
        LONG HighPart;
    } u;
    LONGLONG QuadPart;
} LARGE_INTEGER;

And create functions that take parameters of this type and return results in structs of this type. You could also wrap these operations in a C++ class that will let you define operator overloads that let the expressions look more natural. But I'd look at the already made libraries (like GMP) to see if they can be used - it may save you a lot of work.

I just hope you don't need to implement division using structures like this in straight C - it's painful and runs slow.

Michael Burr
  • 333,147
  • 50
  • 533
  • 760
  • Thanks for the reply. Since it is a power hungry system and the code algorithm is already very slow.. I can't use other libraries. – Viks Apr 18 '10 at 05:21