I'm writing a compressor for a long stream of 128 bit numbers. I would like to store the numbers as differences -- storing only the difference between the numbers rather than the numbers themselves because I can pack the differences in fewer bytes because they are smaller.
However, for compression then I need to subtract these 128 bit values, and for decompression I need to add these values. Maximum integer size for my compiler is 64 bits wide.
Anyone have any ideas for doing this efficiently?
In the same way that compilers emulate e.g. 64-bit integer arithmetic on architectures with register sizes less than 64 bits, some compilers also support 128-bit integer arithmetic. For example, the GCC C compiler 4.6 and later has a 128-bit integer type __int128 for some architectures.
If you only need to store it then you can store it in a byte array like "char num128[16]". If you need to manipulate it you need to use big numbers library like GMP. Show activity on this post. It is not possible to store it in one primitive data type, so we have to be slightly more creative.
The 128-bit data type can handle up to 31 significant digits (compared to 17 handled by the 64-bit long double).
Although GCC does provide __int128 , it is supported only for targets (processors) which have an integer mode wide enough to hold 128 bits. On a given system, sizeof() intmax_t and uintmax_t determine the maximum value that the compiler and the platform support.
If all you need is addition and subtraction, and you already have your 128-bit values in binary form, a library might be handy but isn't strictly necessary. This math is trivial to do yourself.
I don't know what your compiler uses for 64-bit types, so I'll use INT64 and UINT64 for signed and unsigned 64-bit integer quantities.
class Int128 { public: ... Int128 operator+(const Int128 & rhs) { Int128 sum; sum.high = high + rhs.high; sum.low = low + rhs.low; // check for overflow of low 64 bits, add carry to high if (sum.low < low) ++sum.high; return sum; } Int128 operator-(const Int128 & rhs) { Int128 difference; difference.high = high - rhs.high; difference.low = low - rhs.low; // check for underflow of low 64 bits, subtract carry to high if (difference.low > low) --difference.high; return difference; } private: INT64 high; UINT64 low; };
Take a look at GMP.
#include <stdio.h> #include <gmp.h> int main (int argc, char** argv) { mpz_t x, y, z; char *xs, *ys, *zs; int i; int base[4] = {2, 8, 10, 16}; /* setting the value of x in base 10 */ mpz_init_set_str(x, "100000000000000000000000000000000", 10); /* setting the value of y in base 16 */ mpz_init_set_str(y, "FF", 16); /* just initalizing the result variable */ mpz_init(z); mpz_sub(z, x, y); for (i = 0; i < 4; i++) { xs = mpz_get_str(NULL, base[i], x); ys = mpz_get_str(NULL, base[i], y); zs = mpz_get_str(NULL, base[i], z); /* print all three in base 10 */ printf("x = %s\ny = %s\nz = %s\n\n", xs, ys, zs); free(xs); free(ys); free(zs); } return 0; }
The output is
x = 10011101110001011010110110101000001010110111000010110101100111011111000000100000000000000000000000000000000 y = 11111111 z = 10011101110001011010110110101000001010110111000010110101100111011111000000011111111111111111111111100000001 x = 235613266501267026547370040000000000 y = 377 z = 235613266501267026547370037777777401 x = 100000000000000000000000000000000 y = 255 z = 99999999999999999999999999999745 x = 4ee2d6d415b85acef8100000000 y = ff z = 4ee2d6d415b85acef80ffffff01
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With