我需要查找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')是真的。
答案 0 :(得分:6)
目前,我将假设输入是有符号整数。
在这种情况下,我们想从检查标志开始。如果一方是负面的而另一方是正面的,这足以告诉我们结果(负面明显小于正面),所以我们已经完成了。
如果等式的两边都是正的或者都是负的,我们缓存结果的符号,然后去除符号,这样我们就可以处理乘法本身的无符号数。
一旦我们有无符号数,我们可以通过将每个32位整数视为两个不同数字的总和来进行乘法,一个表示输入数字的低位和一个高位。因此,您需要将a
,b
,c
和d
中的每一个转换为仅包含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所以这是假的。