9

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.

p.g.
  • 315
  • 1
  • 10

1 Answers1

1

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.

Community
  • 1
  • 1
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256