I gave some feedback on an earlier version of this, but there was one thing that I didn’t go into detail about.
The Double_Word Type
Currently, you use this in four places: addition, subtraction, multiplication and division. You’ve moved the definition (which previously had some problems) into a different file that you didn’t show us.
Addition and subtraction don’t technically need this type, although it’s likely faster than the alternatives. If the result of an ddition is larger than either of its addends, it overflowed, and you can carry one bit.
Division doesn’t need it either; you can do unsigned long division using only / and %, although some architectures might have speed-ups.
So that leaves multiplication, where you need some way to get the high word of the product. You have a few imperfect options:
- For language lawyers, the only completely compatible, standard-conforming solution is to keep an array of
unsigned short intand widen them tounsigned long int. Ashortis guaranteed to be no more than 16 bits wide, and alongno less than 32 bits wide, so these will work to hold all the bits of the product. There isn’t any larger type guaranteed to be half the size of some other type;int,long,long long,uint_least32_t,uint_least64_tandintmax_tare all allowed to be 64 bits or more wide, anduint32_tis not guaranteed to exist. - In practical, real-world use, all compilers provide
uint32_tanduint64_t. This is an especially good option if you want a simple learning exercise and will not use this in production. However, - It would be more efficient to store data in chunks of the native word size of the target machine. In the real world,
size_tholds an unsigned machine word and operations on them will be fast. A good way to test how wide this is in the preprocessor is the constantSIZE_MAXin<stdint.h>. - When
size_tis larger than 32 bits (#if SIZE_MAX > 0xFFFFFFFFUL), there isn’t a standard type guaranteed to be large enough to hold the product of two of them. However, most compilers offer extensions that solve this:uintmax_tis theoretically supposed to be the widest supported unsigned integral type (although this is frequently not true), so if any type can hold the square ofSIZE_MAX, it might be this. And you can check portably.- GCC, Clang and ICX have an
unsigned __int128type. - MSVC for 64-bit targets has a
__umulhintrinsic function in<intrin.h>that returns the upper word of the product of two unsigned 64-bit numbers, and others for add-with carry. You could use these as non-portable alternatives to a double word. - Some compilers have
uint128_tas an extension, which might be added to the Standard in the future.