我用c++写了一个程序来寻找ab = C的所有解,其中a, b和C一起使用所有的数字0-9,只使用一次。程序循环遍历a和b的值,并每次对a、b和ab运行数字计数例程,以检查是否满足数字条件。
但是,当ab超出整数限制时,会产生伪解。我最终使用如下代码来检查这个:
unsigned long b, c, c_test;
...
c_test=c*b; // Possible overflow
if (c_test/b != c) {/* There has been an overflow*/}
else c=c_test; // No overflow
是否有更好的方法来测试溢出?我知道有些芯片有一个内部标志,在溢出发生时设置,但我从未见过通过C或c++访问它。
注意,有符号int溢出在C和c++中是未定义的行为,因此您必须在不实际引起它的情况下检测它。对于加法前的有符号整型溢出,请参见在C/ c++中检测有符号溢出。
这里有一个“不可移植”的解决方案。Intel x86和x64 cpu有所谓的eflags寄存器,在每次整数算术运算后由处理器填充。我将跳过这里的详细描述。相关的标志是“溢出”标志(掩码0x800)和“携带”标志(掩码0x1)。为了正确地解释它们,应该考虑操作数是有符号类型还是无符号类型。
下面是一个从C/ c++中检查标志的实用方法。下面的代码可以在Visual Studio 2005或更新版本(32位和64位)上运行,也可以在GNU C/ c++ 64位上运行。
#include <cstddef>
#if defined( _MSC_VER )
#include <intrin.h>
#endif
inline size_t query_intel_x86_eflags(const size_t query_bit_mask)
{
#if defined( _MSC_VER )
return __readeflags() & query_bit_mask;
#elif defined( __GNUC__ )
// This code will work only on 64-bit GNU-C machines.
// Tested and does NOT work with Intel C++ 10.1!
size_t eflags;
__asm__ __volatile__(
"pushfq \n\t"
"pop %%rax\n\t"
"movq %%rax, %0\n\t"
:"=r"(eflags)
:
:"%rax"
);
return eflags & query_bit_mask;
#else
#pragma message("No inline assembly will work with this compiler!")
return 0;
#endif
}
int main(int argc, char **argv)
{
int x = 1000000000;
int y = 20000;
int z = x * y;
int f = query_intel_x86_eflags(0x801);
printf("%X\n", f);
}
如果操作数相乘而没有溢出,则query_intel_eflags(0x801)将得到0的返回值,即既没有设置进位标志,也没有设置溢出标志。在提供的main()示例代码中,发生溢出,并且两个标志都被设置为1。这个检查并不意味着任何进一步的计算,所以它应该相当快。
为了扩展Head Geek的答案,有一种更快的方法来执行addition_is_safe;
bool addition_is_safe(unsigned int a, unsigned int b)
{
unsigned int L_Mask = std::numeric_limits<unsigned int>::max();
L_Mask >>= 1;
L_Mask = ~L_Mask;
a &= L_Mask;
b &= L_Mask;
return ( a == 0 || b == 0 );
}
这使用了机器架构安全,64位和32位无符号整数仍然可以正常工作。基本上,我创建了一个掩码,它将屏蔽除最重要的位外的所有内容。然后,对两个整数进行掩码,如果其中任何一个没有设置该位,则加法是安全的。
如果在某个构造函数中预初始化掩码,这将更快,因为它永远不会改变。
有一种方法可以确定一个操作是否可能溢出,使用操作数中最高位的位置和一些基本的二进制数学知识。
对于加法,任何两个操作数的结果(最多)比最大操作数的最高1位多1位。例如:
bool addition_is_safe(uint32_t a, uint32_t b) {
size_t a_bits=highestOneBitPosition(a), b_bits=highestOneBitPosition(b);
return (a_bits<32 && b_bits<32);
}
对于乘法,任何两个操作数的结果(最多)是操作数的位数之和。例如:
bool multiplication_is_safe(uint32_t a, uint32_t b) {
size_t a_bits=highestOneBitPosition(a), b_bits=highestOneBitPosition(b);
return (a_bits+b_bits<=32);
}
类似地,你可以像这样估计a的b次方结果的最大大小:
bool exponentiation_is_safe(uint32_t a, uint32_t b) {
size_t a_bits=highestOneBitPosition(a);
return (a_bits*b<=32);
}
(当然,用比特数代替目标整数。)
我不确定确定数字中最高的1位位置的最快方法,这里有一个蛮力方法:
size_t highestOneBitPosition(uint32_t a) {
size_t bits=0;
while (a!=0) {
++bits;
a>>=1;
};
return bits;
}
它不是完美的,但它能让你在做运算之前知道是否有两个数会溢出。我不知道它是否会比您建议的方式检查结果更快,因为highestOneBitPosition函数中的循环,但它可能会(特别是如果您事先知道操作数中有多少位)。