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.
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.
We need to split numbers to avoid 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.
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.
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.
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