I am looking for an efficient (optionally standard, elegant and easy to implement) solution to multiply relatively large numbers, and store the result into one or several integers :
Let say I have two 64 bits integers declared like this :
uint64_t a = xxx, b = yyy;
When I do a * b
, how can I detect if the operation results in an overflow and in this case store the carry somewhere?
Please note that I don't want to use any large-number library since I have constraints on the way I store the numbers.
The pseudocode to check against overflow for positive numbers follows: if (a > max_int64 / b) then "overflow" else "ok". To handle zeroes and negative numbers you should add more checks. To calculate carry we can use approach to split number into two 32-digits and multiply them as we do this on the paper.
Write a “C” function, int addOvf(int* result, int a, int b) If there is no overflow, the function places the resultant = sum a+b in “result” and returns 0. Otherwise it returns -1. The solution of casting to long and adding to find detecting the overflow is not allowed.
Suppose we want to find the result after multiplying two numbers A and B. We have to check whether the multiplied value will exceed the 64-bit integer or not. If we multiply 100, and 200, it will not exceed, if we multiply 10000000000 and -10000000000, it will overflow.
1. Detecting the overflow:
x = a * b; if (a != 0 && x / a != b) { // overflow handling }
Edit: Fixed division by 0
(thanks Mark!)
2. Computing the carry is quite involved. One approach is to split both operands into half-words, then apply long multiplication to the half-words:
uint64_t hi(uint64_t x) { return x >> 32; } uint64_t lo(uint64_t x) { return ((1ULL << 32) - 1) & x; } void multiply(uint64_t a, uint64_t b) { // actually uint32_t would do, but the casting is annoying uint64_t s0, s1, s2, s3; uint64_t x = lo(a) * lo(b); s0 = lo(x); x = hi(a) * lo(b) + hi(x); s1 = lo(x); s2 = hi(x); x = s1 + lo(a) * hi(b); s1 = lo(x); x = s2 + hi(a) * hi(b) + hi(x); s2 = lo(x); s3 = hi(x); uint64_t result = s1 << 32 | s0; uint64_t carry = s3 << 32 | s2; }
To see that none of the partial sums themselves can overflow, we consider the worst case:
x = s2 + hi(a) * hi(b) + hi(x)
Let B = 1 << 32
. We then have
x <= (B - 1) + (B - 1)(B - 1) + (B - 1) <= B*B - 1 < B*B
I believe this will work - at least it handles Sjlver's test case. Aside from that, it is untested (and might not even compile, as I don't have a C++ compiler at hand anymore).
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