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


当前回答

不能从C/ c++中访问溢出标志。

有些编译器允许你在代码中插入陷阱指令。在GCC上,选项是-ftrapv。

唯一可移植且与编译器无关的事情是自己检查溢出。就像你的例子一样。

但是,使用最新的GCC, -ftrapv似乎在x86上什么都不做。我猜这是一个旧版本的残余,或者特定于其他一些架构。我曾期望编译器在每次添加后插入一个INTO操作码。不幸的是,它不能这样做。

其他回答

如果您有一个比您想要测试的数据类型大的数据类型(比如您做了一个32位的添加,而您有一个64位的类型),那么这将检测是否发生溢出。我的例子是一个8位的添加。但它可以放大。

uint8_t x, y;    /* Give these values */
const uint16_t data16    = x + y;
const bool carry        = (data16 > 0xFF);
const bool overflow     = ((~(x ^ y)) & (x ^ data16) & 0x80);

它基于本页解释的概念:http://www.cs.umd.edu/class/spring2003/cmsc311/Notes/Comb/overflow.html

对于一个32位的例子,0xFF变成0xFFFFFFFF, 0x80变成0x80000000,最后uint16_t变成uint64_t。

注意:这捕获整数加法/减法溢出,我意识到你的问题涉及乘法。在这种情况下,分裂可能是最好的办法。这通常是calloc实现确保参数在相乘以获得最终大小时不会溢出的一种方式。

测试溢出的简单方法是通过检查当前值是否小于前一个值来进行验证。例如,假设你有一个循环输出2的幂:

long lng;
int n;
for (n = 0; n < 34; ++n)
{
   lng = pow (2, n);
   printf ("%li\n", lng);
}

添加溢出检查的方式,我描述的结果如下:

long signed lng, lng_prev = 0;
int n;
for (n = 0; n < 34; ++n)
{
    lng = pow (2, n);
    if (lng <= lng_prev)
    {
        printf ("Overflow: %i\n", n);
        /* Do whatever you do in the event of overflow.  */
    }
    printf ("%li\n", lng);
    lng_prev = lng;
}

它既适用于无符号值,也适用于正负符号值。

当然,如果您想对递减值而不是递增值执行类似的操作,您可以将<=符号翻转,使其为>=,假设下溢的行为与溢出的行为相同。坦率地说,这是在不访问CPU溢出标志的情况下所获得的可移植性(这将需要内联汇编代码,使您的代码在实现之间无法移植)。

在C中捕获整数溢出指出了一种比CERT讨论的更通用的解决方案(就处理的类型而言,它更通用),即使它需要一些GCC扩展(我不知道它们有多广泛的支持)。

为了扩展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位无符号整数仍然可以正常工作。基本上,我创建了一个掩码,它将屏蔽除最重要的位外的所有内容。然后,对两个整数进行掩码,如果其中任何一个没有设置该位,则加法是安全的。

如果在某个构造函数中预初始化掩码,这将更快,因为它永远不会改变。

不能从C/ c++中访问溢出标志。

有些编译器允许你在代码中插入陷阱指令。在GCC上,选项是-ftrapv。

唯一可移植且与编译器无关的事情是自己检查溢出。就像你的例子一样。

但是,使用最新的GCC, -ftrapv似乎在x86上什么都不做。我猜这是一个旧版本的残余,或者特定于其他一些架构。我曾期望编译器在每次添加后插入一个INTO操作码。不幸的是,它不能这样做。