views:

273

answers:

2

I am trying to implement a simple, moderately efficient bignum library in C. I would like to store digits using the full register size of the system it's compiled on (presumably 32 or 64-bit ints). My understanding is that I can accomplish this using intptr_t. Is this correct? Is there a more semantically appropriate type, i.e. something like intword_t?

I also know that with GCC I can easily do overflow detection on a 32-bit machine by upcasting both arguments to 64-bit ints, which will occupy two registers and take advantage of instructions like IA31 ADC (add with carry). Can I do something similar on a 64-bit machine? Is there a 128-bit type I can upcast to which will compile to use these instructions if they're available? Better yet, is there a standard type that represents twice the register size (like intdoubleptr_t) so this could be done in a machine independent fashion?

Thanks!

A: 

I'd strongly recommend using the C99 <stdint.h> header. It declares int32_t, int64_t, uint32_t, and uint64_t, which look like what you really want to use.

EDIT: As Alok points out, int_fast32_t, int_fast64_t, etc. are probably what you want to use. The number of bits you specify should be the minimum you need for the math to work, i.e. for the calculation to not "roll over".

The optimization comes from the fact that the CPU doesn't have to waste cycles realigning data, padding the leading bits on a read, and doing a read-modify-write on a write. Truth is, a lot of processors (such as recent x86s) have hardware in the CPU that optimizes these access pretty well (at least the padding and read-modify-write parts), since they're so common and usually only involve transfers between the processor and cache.

So the only thing left for you to do is make sure the accesses are aligned: take sizeof(int_fast32_t) or whatever and use it to make sure your buffer pointers are aligned to that.

Truth is, this may not amount to that much improvement (due to the hardware optimizing transfers at runtime anyway), so writing something and timing it may be the only way to be sure. Also, if you're really crazy about performance, you may need to look at SSE or AltiVec or whatever vectorization tech your processor has, since that will outperform anything you can write that is portable when doing vectored math.

Mike DeSimone
The problem is I'd like digits to be stored using int32_t on 32-bit machines and int64_t on 64-bit machines. Is the following a better way to accomplish that than using the intptr_t type?:#if __WORDSIZE == 64 typedef int64_t intword_t#else typedef int32_t intword_t#endifAlso, on a 32-bit machine, I can always upcast int32_t to int64_t to ensure addition, etc do not overflow, however on a 64-bit machine, is there a 128-bit type I can upcast to in order to avoid overflows?
datkin
C99 defines `int_fast64_t`, `int_fast32_t`, etc. So maybe they'll be useful? Since your goal is to implement a moderately efficient library, I would say go with one of the above and then optimize later as needed.
Alok
My understanding is that the int_fastXX_t types just "alias" to whatever the natural int type for your machine is (except for when XX is greater than your machine's native int width). Thus int_fast8_t will be 32-bits on a 32-bit machine and 64-bits on a 64-bit machine. Do these types provide other optimizations or is that all?
datkin
@datkin, yes: `int_fast32_t` may in fact be `int64_t`. But they don't provide any other optimization. The reason I suggested this because you mentioned optimization in your original question. In any case, since you're just "trying things out", you should pick one type and then optimize later.
Alok
+1  A: 

Any reason not to use size_t? size_t is 4 bytes on a 32-bit system and 8 bytes on a 64-bit system, and is probably more portable than using WORD_SIZE (I think WORD_SIZE is gcc-specific, no?)

I am not aware of any 128-bit value on 64-bit systems, could be wrong here but haven't come across that type in the kernel or regular user apps.

Sam Post
I got the impression from this that intptr_t is slightly more portable for this purpose than size_t: http://stackoverflow.com/questions/1464174/sizet-vs-intptrtOf course, I'm not looking to store pointers, I'm looking to store an integer that takes up a whole register.
datkin
@datkin: The question in your link is specifically about storing pointers. For your case, `size_t` is better than `intptr_t`, or use `[u]int[_fast]N_t` types.
Alok
I know this is just an excercise, but how important is portability? If it's not important then maybe you don't care about size_t. Also I have had issues using size_t in #defines
Sam Post