Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Combined multiply divide operation on 64bit signed integer without overflow

I need to calculate

result = (dividend * factor) / divisor

where

dividend: full range of int64_t values
factor: either a full range of uint32_t values or as a special case 2^32
divisor: positive values of int64_t
result: is guaranteed to fit in a int32_t

I need to do this in plain C/C++ without any libraries on a microcontroller. The compiler supports int64_t and uint64_t types; most likely no hardware implementation for multiply or divide. Currently I have a workaround for the uint32_t factor but I need a solution for factor 2^32.

like image 202
p.g. Avatar asked Dec 28 '15 18:12

p.g.


People also ask

How do you multiply integers without overflow?

Approach : If either of the number is 0, then it will never exceed the range. Else if the product of the two divided by one equals the other, then also it will be in range. In any other case overflow will occur.

How do you stop overflow multiplication in C++?

We need to split numbers to avoid overflow.

Does unsigned INT overflow?

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.

Can division cause arithmetic overflow?

Overflow can also occur during two's complement signed integer division when the dividend is equal to the minimum (most negative) value for the signed integer type and the divisor is equal to −1.


1 Answers

OP: "Currently I have a workaround for the uint32_t factor"

The factor == 2^32 is a corner case and is all that is needed to be solved here as OP's "workaround" can handle factors [0 ... 2^32-1].

If the dividend can be doubled without overflow, simple use factor == 2^31 with a doubled dividend.

If the divisor is even, use factor == 2^31 with a halved divisor. @Weather Vane

Else the dividend is large. Recall that the quotient is in [-2^31 ... 2^31-1] range. In general, the product of the large dividend and factor == 2^32 dividend by the divisor will out of the int32_t range, so those out-of-range combinations are not of concern as "result: is guaranteed to fit in a int32_t".

The acceptable edge conditions occur with a final quotient near the edge of the int32_t range.

 pow(2,63) == 9223372036854775808
 pow(2,62) == 4611686018427387904
 pow(2,32) == 4294967296
 pow(2,31) == 2147483648

 Smallest Dividends   Factor      Largest Divisors       Smallest Quotients 
-4611686018427387905  4294967296  -9223372036854775807   2147483648.00+
-4611686018427387905  4294967296   9223372036854775807  -2147483648.00+  OK
 4611686018427387904  4294967296  -9223372036854775807  -2147483648.00+  OK
 4611686018427387904  4294967296   9223372036854775807   2147483648.00+

After testing, dividend and divisor, the only representable answer in INT32_MIN.


Sample Code:

int32_t muldiv64(int64_t dividend, uint64_t factor, int64_t divisor) {
  if (factor >= 0x100000000) {
    assert(factor == 0x100000000);
    factor /= 2;
    if (dividend >= INT64_MIN/2 && dividend <= INT64_MAX/2) {
      dividend *= 2;
    } else if (divisor %2 == 0) {
      divisor /= 2;
    } else {
      return INT32_MIN;
    }
  }
  return  workaround_for_the_uint32_t_factor(dividend, factor, divisor);
}

The original problem is to detect this edge condition and how to handle it.. The workaround_for_the_uint32_t_factor() may not have been coded yet, thus it was not posted.

like image 115
chux - Reinstate Monica Avatar answered Sep 30 '22 16:09

chux - Reinstate Monica