开发者

Doing 64 bit manipulation using 32 bit data in Fixed point arithmetic using C

开发者 https://www.devze.com 2022-12-27 01:41 出处:网络
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.

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 i开发者_运维技巧t 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


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.


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)))


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


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.


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.

0

精彩评论

暂无评论...
验证码 换一张
取 消