比较两个没有溢出的整数产品

时间:2014-12-04 20:56:06

标签: c++

我需要查找a*b >= c*d a,b,c,d是否有32位整数(我的机器上是'int')。

是否可以仅使用32位有符号整数比较那些没有溢出的产品,以便结果对所有可能的值都正确?

我想到了a/d >= c/b

然而它在'2 * 7> = 3 * 5'(假)上失败,因为'2/5> = 3/7'('0> = 0')是真的。

3 个答案:

答案 0 :(得分:6)

目前,我将假设输入是有符号整数。

在这种情况下,我们想从检查标志开始。如果一方是负面的而另一方是正面的,这足以告诉我们结果(负面明显小于正面),所以我们已经完成了。

如果等式的两边都是正的或者都是负的,我们缓存结果的符号,然后去除符号,这样我们就可以处理乘法本身的无符号数。

一旦我们有无符号数,我们可以通过将每个32位整数视为两个不同数字的总和来进行乘法,一个表示输入数字的低位和一个高位。因此,您需要将abcd中的每一个转换为仅包含16位有效位的两个数字。所以,对于左侧,我们有:

al = a & 0xffff;
au = a >> 16;

bl = b & 0xffff;
bu = b >> 16;

所以:

a * b 

...与:

相同
(al + au << 16) * (bl + bu << 16)

并使用分配属性,我们可以将其转换为:

al * bl + au<<16 * bl + al * bu<<16 + au<<16 * bu<<16

由于a * (b * c) = (a * b) * c,我们可以在进行其他乘法后完成所有的位移,因此变为:

al * bl +            // we'll call this intermediate result "lower"
(au * bl) << 16 +
(al * bu) << 16 +    // we'll call the sum of these two "mid" 
(au * bu) << 32      // we'll call this one "upper"

现在重要一点:我们的位掩码确保每个乘法步骤的输入只有16个有效位,因此每个中间结果只有32个有效位,因此每个都将适合单个32位整数四溢。

从那里,我们必须总结这些条款。这有点不重要,但仍然相当容易处理。首先,我们必须弄清楚一个术语的总和是否会产生一个进位。一种方法是这样的:

bool carry(unsigned a, unsigned b) { 
    return a > (std::number_limits<unsigned>::max() - b);
}

然后我们的结果是低+中&lt;&lt; 16&lt;&lt;&lt; 32.由于我们处理的是32位整数,因此最简单的方法是将mid分成上半部分和下半部分。它的下半部分将添加到lower,其上半部分将添加到upper。然后,我们的结果将分布在两个(无符号)32位整数中,一个包含lower + mid_lower,另一个包含upper + mid_upper + carries

从那里可以很简单地恢复我们在开始时存储的标志,然后比较上半部分,当且仅当它们相等时,比较下半部分。

如果您的号码开头是无符号的,那么您可以轻易跳过涉及标志的部分。

答案 1 :(得分:0)

选项1

使用bigint库。 Boost has one.

#include <boost/multiprecision/cpp_int.hpp>
bool BigintCompareProducts(int a, int b, int c, int d)
{
  using boost::multiprecision::cpp_int;
  return cpp_int(a) * cpp_int(b) >= cpp_int(c) * cpp_int(d);
}

选项2

建立a/d >= c/b想法,您可以添加逻辑来检查余数。我没有对此进行过广泛的测试,目前也没有负数。

// Returns 1, 0, or -1 if a is >, ==, or < b
template<typename T>
int cmp(T a, T b)
{
  return a > b ? 1 : (a < b ? -1 : 0);
}

// Returns 1, 0, or -1 if n1/d1 is >, ==, or < n2/d2
int CompareFractions(int n1, int d1, int n2, int d2)
{
  int f1 = n1 / d1;
  int f2 = n2 / d2;
  int result = cmp(f1, f2);
  if (result != 0) {
    return result;
  }

  // Equal fractions - remainder may make them different
  int r1 = n1 % d1;
  int r2 = n2 % d2;
  if (r1 == 0 || r2 == 0) {
    // Any zero remainder is less than any positive fraction.
    return cmp(r1, r2);
  } else {
    return -1 * CompareFractions(d1, n1 % d1, d2, n2 % d2);
  }
}

// Returns 1, 0, or -1 if a * b >, ==, or < c * d
int CompareProducts(int a, int b, int c, int d)
{
  return CompareFractions(a, d, c, b);
}

答案 2 :(得分:0)

不确定完全您的需求,但您应该尝试这样做:

int a, b, c, d;
// set them for a*b=c*d

int one = a/d, two = c/b
int greatest = one;
if (two > greatest) greatest = two;
int k = pow(10.0, 8-log(greatest)); // log (INT_MAX) = 9

one = k*a/d;
two = k*c/b;

// if one > two then a*b > c*d

对于2×7&gt; = 3×5,你得到了40000000&gt; = 42857142所以这是假的。