在64位有符号整数上组合乘除运算,无溢出

时间:2015-12-28 18:35:50

标签: c algorithm biginteger division bignum

我需要计算

INSERT INTO job_role_dim
SELECT job_role_id.nextval, job_role_desc
FROM (
  SELECT DISTINCT job_role_desc
  FROM temp_job_role_dim
);

,其中

result = (dividend * factor) / divisor

我需要在没有微控制器上的任何库的普通C / C ++中执行此操作。 编译器支持int64_t和uint64_t类型;很可能没有用于乘法或除法的硬件实现。 目前我有uint32_t因子的解决方法,但我需要因子2 ^ 32的解决方案。

1 个答案:

答案 0 :(得分:1)

  

OP:“目前我有uint32_t因子的解决方法”

[0 ... 2^32-1]是一个极端情况,因为OP的“变通方法”可以处理因子dividend,所以这里需要解决所有问题。

如果factor == 2^31可以加倍而不会溢出,只需使用加倍dividend的{​​{1}}。

如果divisor是偶数,请将factor == 2^31与减半divisor一起使用。 @Weather Vane

否则dividend很大。回想一下,商在[-2^31 ... 2^31-1]范围内。一般来说,dividend的大factor == 2^32divisor红利的乘积将超出int32_t范围,因此那些超出范围的组合不受关注因为“结果:保证适合int32_t”。

可接受的边缘条件出现在int32_t范围边缘附近的最终商。

 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+

经过测试dividenddivisorINT32_MIN中唯一可表示的答案。

示例代码:

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);
}

最初的问题是检测此边缘条件以及如何处理它。workaround_for_the_uint32_t_factor()可能尚未编码,因此未发布。