我有以下变量:
double dblVar1;
double dblVar2;
它们可能有很大的值,但小于double
最大值。
我对上述变量有各种算术,如加法、乘法和幂:
double dblVar3 = dblVar1 * dblVar2;
double dblVar4 = dblVar1 + dblVar2;
double dblVar5 = pow(dblVar1, 2);
综上所述,我必须检查上溢和下溢。如何在 C++ 中实现这一点?
我有以下变量:
double dblVar1;
double dblVar2;
它们可能有很大的值,但小于double
最大值。
我对上述变量有各种算术,如加法、乘法和幂:
double dblVar3 = dblVar1 * dblVar2;
double dblVar4 = dblVar1 + dblVar2;
double dblVar5 = pow(dblVar1, 2);
综上所述,我必须检查上溢和下溢。如何在 C++ 中实现这一点?
很大程度上取决于上下文。为了完美便携,您必须在操作前检查,例如(用于添加):
if ( (a < 0.0) == (b < 0.0)
&& std::abs( b ) > std::numeric_limits<double>::max() - std::abs( a ) ) {
// Addition would overflow...
}
类似的逻辑可用于四个基本运算符。
如果您所针对的所有机器都支持 IEEE(如果您不必考虑大型机,可能就是这种情况),您可以只执行操作,然后在结果上使用isfinite
或isinf
。
对于下溢,第一个问题是逐渐下溢是否算作下溢。如果不是,那么只需检查结果是否为零即可a != -b
。如果您想检测逐渐下溢(这可能仅在您有 IEEE 时才存在),那么您可以使用isnormal
— 如果结果对应于逐渐下溢,这将返回 false。(与溢出不同,您在操作后测试下溢。)
POSIX、C99、C++11 <fenv.h>
(和<cfenv>
对于 C++11)具有测试 IEEE754 异常标志的功能(与 C++ 异常无关,这太容易了):
int feclearexcept(int);
int fegetexceptflag(fexcept_t *, int);
int feraiseexcept(int);
int fesetexceptflag(const fexcept_t *, int);
int fetestexcept(int);
该标志是一个定义了以下位的位域:
FE_DIVBYZERO
FE_INEXACT
FE_INVALID
FE_OVERFLOW
FE_UNDERFLOW
因此,您可以在操作前清除它们,然后再进行测试。您必须检查文档以了解库函数对它们的影响。
使用像样的编译器(支持最新的 C++ 标准),您可以使用以下函数:
#include <cfenv>
#include <iostream>
int main() {
std::feclearexcept(FE_OVERFLOW);
std::feclearexcept(FE_UNDERFLOW);
double overflowing_var = 1000;
double underflowing_var = 0.01;
std::cout << "Overflow flag before: " << (bool)std::fetestexcept(FE_OVERFLOW) << std::endl;
std::cout << "Underflow flag before: " << (bool)std::fetestexcept(FE_UNDERFLOW) << std::endl;
for(int i = 0; i < 20; ++i) {
overflowing_var *= overflowing_var;
underflowing_var *= underflowing_var;
}
std::cout << "Overflow flag after: " << (bool)std::fetestexcept(FE_OVERFLOW) << std::endl;
std::cout << "Underflow flag after: " << (bool)std::fetestexcept(FE_UNDERFLOW) << std::endl;
}
/** Output:
Overflow flag before: 0
Underflow flag before: 0
Overflow flag after: 1
Underflow flag after: 1
*/
ISO C99 定义了查询和操作浮点状态字的函数。您可以在方便时使用这些函数来检查未捕获的异常,而不是在计算过程中担心它们。
它提供
FE_INEXACT
FE_DIVBYZERO
FE_UNDERFLOW
FE_OVERFLOW
FE_INVALID
例如
{
double f;
int raised;
feclearexcept (FE_ALL_EXCEPT);
f = compute ();
raised = fetestexcept (FE_OVERFLOW | FE_INVALID);
if (raised & FE_OVERFLOW) { /* ... */ }
if (raised & FE_INVALID) { /* ... */ }
/* ... */
}
http://www.gnu.org/software/libc/manual/html_node/Status-bit-operations.html