views:

690

answers:

4

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 result in an overflow and in this case store the carry somewhere ?

Please note that I don't what the use any large-number library since I have constraints on the way I store the numbers.

Thanks

+9  A: 

Detection:

x = a * b;
if (a != 0 && x / a != b) {
    // overflow handling
}

Edit: Fixed division by 0 (thanks Mark!)

Carry:

uint64_t carry = hi(a) * hi(b) + hi(hi(a) * lo(b)) + hi(lo(a) * hi(b));

where

hi(x) = x >> 32;
lo(x) = ((1 << 32) - 1) & x;

Acknowledgement: Idea gleaned from the implementation of java.math.BigDecimal

meriton
wow thank you....
Ben
The problem with this answer is that signed integer overflow causes undefined behaviour in C - you can't rely on your code continuing to execute correctly (or at all!) after such an event. (On some platforms, overflow triggers a program abort in a similar way to divide-by-zero). sergdev's answer is the right one.
caf
caf’s comment is incorrect. The C99 standard mandates that “A computation involving unsigned operands can never overflow, because a result that cannot be represented by the resulting unsigned integer type is reduced modulo the number that is one greater than the largest value that can be represented by the resulting type.” As such, meriton’s solution is valid in theory as well as practice.
Ahruman
+3  A: 

A version that also works when a == 0:

    x = a * b;
    if (a != 0 && x / a != b) {
        // overflow handling
    }
Mark Byers
good point, will edit.
meriton
+5  A: 

The idea is to use following fact which is true for integral operation:

a*b > c if and only if a > c/b

/ is integral division here.

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.

C code for non-negative a and b follows:

if (b > 0 && a > 18446744073709551615 / b) {
     // overflow handling
}; else {
    c = a * b;
}

Note:

18446744073709551615 == (1<<64)-1

To calculate carry we can use approach to split number into two 32-digits and multiply them as we do this on the paper. We need to split numbers to avoid overflow.

Code follows:

// split input numbers into 32-bit digits
uint64_t a0 = a & ((1LL<<32)-1);
uint64_t a1 = a >> 32;
uint64_t b0 = b & ((1LL<<32)-1);
uint64_t b1 = b >> 32;


// The following 3 lines of code is to calculate the carry of d1
// (d1 - 32-bit second digit of result, and it can be calculated as d1=d11+d12),
// but to avoid overflow.
// Actually rewriting the following 2 lines:
// uint64_t d1 = (a0 * b0 >> 32) + a1 * b0 + a0 * b1;
// uint64_t c1 = d1 >> 32;
uint64_t d11 = a1 * b0 + (a0 * b0 >> 32); 
uint64_t d12 = a0 * b1;
uint64_t c1 = (d11 > 18446744073709551615 - d12) ? 1 : 0;

uint64_t d2 = a1 * b1 + c1;
uint64_t carry = d2; // needed carry stored here
sergdev
This is the right idea, except the `+` operations can overflow...
Norman Ramsey
@Norman Ramsey Ohhhh, I missed this, thanks! Fixed.
sergdev
If your going to need the carry anyway (or need it enough of the time) you might as well just compute it and check for non-zero. Many 32bit system will implement a 64bit multiplication as a slightly trimmed version of it anyway so with a few short-stop checks it might be only a little slower than the direct multiplication.
BCS
+3  A: 
Norman Ramsey