我用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++中检测有符号溢出。


当前回答

要以一种可移植的方式执行无符号乘法而不溢出,可以使用以下方法:

... /* begin multiplication */
unsigned multiplicand, multiplier, product, productHalf;
int zeroesMultiplicand, zeroesMultiplier;
zeroesMultiplicand = number_of_leading_zeroes( multiplicand );
zeroesMultiplier   = number_of_leading_zeroes( multiplier );
if( zeroesMultiplicand + zeroesMultiplier <= 30 ) goto overflow;
productHalf = multiplicand * ( c >> 1 );
if( (int)productHalf < 0 ) goto overflow;
product = productHalf * 2;
if( multiplier & 1 ){
   product += multiplicand;
   if( product < multiplicand ) goto overflow;
}
..../* continue code here where "product" is the correct product */
....
overflow: /* put overflow handling code here */

int number_of_leading_zeroes( unsigned value ){
   int ctZeroes;
   if( value == 0 ) return 32;
   ctZeroes = 1;
   if( ( value >> 16 ) == 0 ){ ctZeroes += 16; value = value << 16; }
   if( ( value >> 24 ) == 0 ){ ctZeroes +=  8; value = value <<  8; }
   if( ( value >> 28 ) == 0 ){ ctZeroes +=  4; value = value <<  4; }
   if( ( value >> 30 ) == 0 ){ ctZeroes +=  2; value = value <<  2; }
   ctZeroes -= x >> 31;
   return ctZeroes;
}

其他回答

#include <stdio.h>
#include <stdlib.h>

#define MAX 100 

int mltovf(int a, int b)
{
    if (a && b) return abs(a) > MAX/abs(b);
    else return 0;
}

main()
{
    int a, b;

    for (a = 0; a <= MAX; a++)
        for (b = 0; b < MAX; b++) {

        if (mltovf(a, b) != (a*b > MAX)) 
            printf("Bad calculation: a: %d b: %d\n", a, b);

    }
}

内联程序集允许您直接检查溢出位。如果你打算使用c++,你真的应该学习汇编。

CERT开发了一种新方法,使用“as-if”无限范围(AIR)整数模型来检测和报告有符号整数溢出、无符号整数包装和整数截断。CERT已经发布了一份描述该模型的技术报告,并生成了一个基于GCC 4.4.0和GCC 4.5.0的工作原型。

AIR整数模型产生的值与使用无限范围整数所获得的值相等,或者导致违反运行时约束。与之前的整数模型不同,AIR整数不需要精确的陷阱,因此不会破坏或抑制大多数现有的优化。

这里有一个“不可移植”的解决方案。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。这个检查并不意味着任何进一步的计算,所以它应该相当快。

最简单的方法是将unsigned long转换为unsigned long,进行乘法运算,并将结果与0x100000000LL进行比较。

你可能会发现这比你在例子中做除法更有效。

哦,它在C和c++中都可以工作(因为你已经用这两种语言标记了问题)。


我在看glibc手册。这里提到了整数溢出陷阱(FPE_INTOVF_TRAP)作为SIGFPE的一部分。这将是理想的,除了手册中令人讨厌的部分:

FPE_INTOVF_TRAP 整数溢出(在C程序中不可能,除非您以特定于硬件的方式启用溢出捕获)。

真的有点遗憾。