我用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++中检测有符号溢出。
我需要为浮点数回答同样的问题,在浮点数中位屏蔽和移位看起来没有希望。我确定的方法适用于有符号和无符号,整数和浮点数。即使没有更大的数据类型可以用于中间计算,它也可以工作。对于所有这些类型,它不是最有效的,但因为它确实适用于所有类型,所以值得使用。
有符号溢出测试,加减法:
Obtain the constants that represent the largest and smallest possible values for the type,
MAXVALUE and MINVALUE.
Compute and compare the signs of the operands.
a. If either value is zero, then neither addition nor subtraction can overflow. Skip remaining tests.
b. If the signs are opposite, then addition cannot overflow. Skip remaining tests.
c. If the signs are the same, then subtraction cannot overflow. Skip remaining tests.
Test for positive overflow of MAXVALUE.
a. If both signs are positive and MAXVALUE - A < B, then addition will overflow.
b. If the sign of B is negative and MAXVALUE - A < -B, then subtraction will overflow.
Test for negative overflow of MINVALUE.
a. If both signs are negative and MINVALUE - A > B, then addition will overflow.
b. If the sign of A is negative and MINVALUE - A > B, then subtraction will overflow.
Otherwise, no overflow.
签名溢出测试,乘法和除法:
Obtain the constants that represent the largest and smallest possible values for the type,
MAXVALUE and MINVALUE.
Compute and compare the magnitudes (absolute values) of the operands to one. (Below, assume A and B are these magnitudes, not the signed originals.)
a. If either value is zero, multiplication cannot overflow, and division will yield zero or an infinity.
b. If either value is one, multiplication and division cannot overflow.
c. If the magnitude of one operand is below one and of the other is greater than one, multiplication cannot overflow.
d. If the magnitudes are both less than one, division cannot overflow.
Test for positive overflow of MAXVALUE.
a. If both operands are greater than one and MAXVALUE / A < B, then multiplication will overflow.
b. If B is less than one and MAXVALUE * B < A, then division will overflow.
Otherwise, no overflow.
注意:MINVALUE的最小溢出由3处理,因为我们取的是绝对值。然而,如果
ABS(MINVALUE) > MAXVALUE,那么我们将会有一些罕见的假阳性。
下溢测试类似,但涉及EPSILON(大于零的最小正数)。
如果您有一个比您想要测试的数据类型大的数据类型(比如您做了一个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实现确保参数在相乘以获得最终大小时不会溢出的一种方式。
要以一种可移植的方式执行无符号乘法而不溢出,可以使用以下方法:
... /* 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;
}
另一种使用汇编语言的解决方案是外部过程。下面是在Linux x64下使用g++和fasm进行无符号整数乘法的示例。
这个过程将两个无符号整数参数相乘(32位)(根据amd64的规范(第3.2.3节参数传递)。
如果类为INTEGER,则使用序列%rdi、%rsi、%rdx、%rcx、%r8和%r9的下一个可用寄存器
(edi和esi寄存器在我的代码)),并返回结果或0,如果发生溢出。
format ELF64
section '.text' executable
public u_mul
u_mul:
MOV eax, edi
mul esi
jnc u_mul_ret
xor eax, eax
u_mul_ret:
ret
测试:
extern "C" unsigned int u_mul(const unsigned int a, const unsigned int b);
int main() {
printf("%u\n", u_mul(4000000000,2)); // 0
printf("%u\n", u_mul(UINT_MAX/2,2)); // OK
return 0;
}
将程序链接到asm对象文件。在我的例子中,在Qt Creator中将它添加到一个.pro文件中的LIBS中。
我需要为浮点数回答同样的问题,在浮点数中位屏蔽和移位看起来没有希望。我确定的方法适用于有符号和无符号,整数和浮点数。即使没有更大的数据类型可以用于中间计算,它也可以工作。对于所有这些类型,它不是最有效的,但因为它确实适用于所有类型,所以值得使用。
有符号溢出测试,加减法:
Obtain the constants that represent the largest and smallest possible values for the type,
MAXVALUE and MINVALUE.
Compute and compare the signs of the operands.
a. If either value is zero, then neither addition nor subtraction can overflow. Skip remaining tests.
b. If the signs are opposite, then addition cannot overflow. Skip remaining tests.
c. If the signs are the same, then subtraction cannot overflow. Skip remaining tests.
Test for positive overflow of MAXVALUE.
a. If both signs are positive and MAXVALUE - A < B, then addition will overflow.
b. If the sign of B is negative and MAXVALUE - A < -B, then subtraction will overflow.
Test for negative overflow of MINVALUE.
a. If both signs are negative and MINVALUE - A > B, then addition will overflow.
b. If the sign of A is negative and MINVALUE - A > B, then subtraction will overflow.
Otherwise, no overflow.
签名溢出测试,乘法和除法:
Obtain the constants that represent the largest and smallest possible values for the type,
MAXVALUE and MINVALUE.
Compute and compare the magnitudes (absolute values) of the operands to one. (Below, assume A and B are these magnitudes, not the signed originals.)
a. If either value is zero, multiplication cannot overflow, and division will yield zero or an infinity.
b. If either value is one, multiplication and division cannot overflow.
c. If the magnitude of one operand is below one and of the other is greater than one, multiplication cannot overflow.
d. If the magnitudes are both less than one, division cannot overflow.
Test for positive overflow of MAXVALUE.
a. If both operands are greater than one and MAXVALUE / A < B, then multiplication will overflow.
b. If B is less than one and MAXVALUE * B < A, then division will overflow.
Otherwise, no overflow.
注意:MINVALUE的最小溢出由3处理,因为我们取的是绝对值。然而,如果
ABS(MINVALUE) > MAXVALUE,那么我们将会有一些罕见的假阳性。
下溢测试类似,但涉及EPSILON(大于零的最小正数)。
尝试这个宏来测试32位机器的溢出位(改编自Angel Sinigersky的解决方案)
#define overflowflag(isOverflow){ \
size_t eflags; \
asm ("pushfl ;" \
"pop %%eax" \
: "=a" (eflags)); \
isOverflow = (eflags >> 11) & 1;}
我将其定义为宏,因为否则溢出位将被覆盖。
下面是上面代码段的一个小应用程序:
#include <cstddef>
#include <stdio.h>
#include <iostream>
#include <conio.h>
#if defined( _MSC_VER )
#include <intrin.h>
#include <oskit/x86>
#endif
using namespace std;
#define detectOverflow(isOverflow){ \
size_t eflags; \
asm ("pushfl ;" \
"pop %%eax" \
: "=a" (eflags)); \
isOverflow = (eflags >> 11) & 1;}
int main(int argc, char **argv) {
bool endTest = false;
bool isOverflow;
do {
cout << "Enter two intergers" << endl;
int x = 0;
int y = 0;
cin.clear();
cin >> x >> y;
int z = x * y;
detectOverflow(isOverflow)
printf("\nThe result is: %d", z);
if (!isOverflow) {
std::cout << ": no overflow occured\n" << std::endl;
} else {
std::cout << ": overflow occured\n" << std::endl;
}
z = x * x * y;
detectOverflow(isOverflow)
printf("\nThe result is: %d", z);
if (!isOverflow) {
std::cout << ": no overflow ocurred\n" << std::endl;
} else {
std::cout << ": overflow occured\n" << std::endl;
}
cout << "Do you want to stop? (Enter \"y\" or \"Y)" << endl;
char c = 0;
do {
c = getchar();
} while ((c == '\n') && (c != EOF));
if (c == 'y' || c == 'Y') {
endTest = true;
}
do {
c = getchar();
} while ((c != '\n') && (c != EOF));
} while (!endTest);
}