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


当前回答

x86指令集包括一个无符号乘法指令,它将结果存储到两个寄存器中。要使用C中的指令,可以在64位程序(GCC)中编写以下代码:

unsigned long checked_imul(unsigned long a, unsigned long b) {
  unsigned __int128 res = (unsigned __int128)a * b;
  if ((unsigned long)(res >> 64))
    printf("overflow in integer multiply");
  return (unsigned long)res;
}

对于32位程序,需要使结果为64位,参数为32位。

另一种方法是使用依赖于编译器的intrinsic来检查标志寄存器。关于溢出的GCC文档可以从6.56内置函数执行溢出检查算术中找到。

其他回答

用double计算结果。它们有15位有效数字。您的要求在c上有一个硬上限108 -它最多可以有8位数字。因此,如果它在范围内,结果将是精确的,否则它将不会溢出。

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

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

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

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

警告:GCC在使用-O2编译时会优化掉溢出检查。 选项-Wall会在某些情况下给你一个警告

if (a + b < a) { /* Deal with overflow */ }

但在这个例子中不是:

b = abs(a);
if (b < 0) { /* Deal with overflow */ }

唯一安全的方法是在溢出发生之前检查溢出,正如CERT论文中所描述的那样,系统地使用这种方法将非常繁琐。

使用-fwrapv编译可以解决这个问题,但会禁用一些优化。

我们迫切需要一个更好的解决方案。我认为编译器应该发出一个警告,默认情况下,优化依赖于溢出没有发生。目前的情况允许编译器优化掉溢出检查,这在我看来是不可接受的。

一些编译器提供了对CPU中整数溢出标志的访问,然后可以测试,但这不是标准的。

您还可以在执行乘法之前测试溢出的可能性:

if ( b > ULONG_MAX / a ) // a * b would overflow

x86指令集包括一个无符号乘法指令,它将结果存储到两个寄存器中。要使用C中的指令,可以在64位程序(GCC)中编写以下代码:

unsigned long checked_imul(unsigned long a, unsigned long b) {
  unsigned __int128 res = (unsigned __int128)a * b;
  if ((unsigned long)(res >> 64))
    printf("overflow in integer multiply");
  return (unsigned long)res;
}

对于32位程序,需要使结果为64位,参数为32位。

另一种方法是使用依赖于编译器的intrinsic来检查标志寄存器。关于溢出的GCC文档可以从6.56内置函数执行溢出检查算术中找到。