这种乘除功能是否正确?

时间:2012-04-25 19:11:32

标签: c++ c algorithm math numbers

我试图在某些计算中避免long long s和整数溢出,所以我想出了下面的函数来计算(a * b) / c(由于截断整数除法,顺序很重要)。

 unsigned muldiv(unsigned a, unsigned b, unsigned c)
 {
      return a * (b / c) + (a * (b % c)) / c;
 }

是否存在任何不能按预期工作的边缘情况?

3 个答案:

答案 0 :(得分:4)

EDITED:对于原始明显逻辑正确的值的超集,这是正确的。如果c>它仍然没有给你买b,可能还有其他条件。也许您对c的价值观有所了解,但这可能没有您期望的那么多。 abc的某些组合仍会溢出。

编辑:假设您因严格的C ++ 98可移植性原因而避免使用long long,那么通过推广您的unsigneddouble,您可以获得大约52位的精确度有积分值来做数学。使用double数学实际上可能比做三个整数除法更快。

答案 1 :(得分:4)

这在很多情况下都失败了。最明显的是当a很大时,a * (b % c)溢出。在这种情况下,您可以尝试更换ab,但如果abc都很大,那么仍然会失败。考虑a = b = 2 ^ 25-1和c = 2 ^ 24,使用32位无符号。正确的结果是2 ^ 26-4,但a * (b % c)b * (a % c)都会溢出。即使(a % c) * (b % c)也会溢出。

到目前为止,解决这个问题的最简单方法是扩大倍数,这样就可以获得更高精度的中间产品。如果你没有那个,你需要用较小的乘法和除法来合成它,这与实现你自己的biginteger库非常相似。

如果你能保证c足够小,(c-1)*(c-1)不会溢出未签名的,你可以使用:

unsigned muldiv(unsigned a, unsigned b, unsigned c) {
    return (a/c)*(b/c)*c + (a%c)*(b/c) + (a/c)*(b%c) + (a%c)*(b%c)/c;
}

这实际上会给你所有a和b的“正确”答案 - (a * b)/ c%(UINT_MAX + 1)

答案 2 :(得分:0)

为了避免溢出,你必须预先划分然后乘以某个因子。

使用的最佳因素是c,只要a和b中的一个(或两个)大于c。这就是克里斯多德的功能。它具有((a%c)*(b%c))的最大中间值,正如Chris所指出的那样,小于或等于((c-1)*(c-1))。

如果你可能遇到a和b都小于c的情况,但是(a * b)仍然可能溢出(当c接近字大小的限制时可能就是这种情况)那么最好的因素是使用是两个的大功率,转向乘法并分为移位。尝试移动一半的字大小。

请注意,使用预分割然后后乘法相当于在没有更长的单词时使用更长的单词。假设你没有丢弃低阶位但只是将它们添加为另一个术语,那么你只是使用几个单词而不是一个更大的单词。

我会让你填写代码。