我需要有关如何以标准方式检测浮点下溢的帮助和示例代码,而不使用第三方库和有符号和无符号的异常。我用谷歌搜索,发现各种作者都在谈论“逐渐下溢算不算下溢”?谁能解释一下什么是逐渐下溢?我需要检查这两种情况
感谢您的时间和帮助
我需要有关如何以标准方式检测浮点下溢的帮助和示例代码,而不使用第三方库和有符号和无符号的异常。我用谷歌搜索,发现各种作者都在谈论“逐渐下溢算不算下溢”?谁能解释一下什么是逐渐下溢?我需要检查这两种情况
感谢您的时间和帮助
查看“逐渐下溢”的热门搜索结果并没有给出明确直接的答案,因此:
IEEE-754 二进制浮点数在它们的大部分范围内都有一个规则模式:有一个符号、一个指数和一个具有一定位数的有效位(24 位代表 32 位float
,53 位代表 64 位double
)。但是,图案必须在末端打断。在高端,对于最大指数来说太大的结果将变为无穷大。在低端,我们有一个选择。
一种选择是,如果结果低于最低指数,则将结果四舍五入为零。但是,IEEE-754 使用了一种不同的方案,称为逐渐下溢。最低指数保留用于与常规指数不同的格式。
对于普通指数,24 位有效数为“1”。后跟在有效位字段中编码的 23 位。当数为次正规数时,指数与最低正则指数具有相同的值,但 24 位有效数为“0”。其次是 23 位。这是逐渐下溢,因为随着数字变小,在我们达到零之前,它们的精度越来越低(有效数字中的更多前导位为零)。
渐进下溢有一些很好的数学性质,特别是a-b == 0
当且仅当a == b
. a-b == 0
对于突然的下溢,即使a
和b
不同,也有可能是因为a-b
太小而无法以浮点格式表示。随着逐渐溢出,所有可能的值a-b
, 对于小的a
和b
, 都是可表示的,因为它们只是具有最低指数的有效数字的差异。
确定是否发生浮点下溢的另一个问题是允许实现(根据 IEEE-754 标准)基于舍入前后的测试报告下溢。在计算结果时,浮点实现实际上必须执行以下步骤:
该标准允许实现通过以下任一方式报告下溢:
或者:
因此,对于相同的计算,两种不同的浮点实现可能会返回关于下溢的不同报告。
(还有一些关于处理下溢的附加规则。上面会导致发出下溢异常信号。但是,如果未启用来自此异常的陷阱并且结果是精确的 [舍入没有改变任何东西],那么“下溢”将被忽略, 并且不引发下溢标志。如果结果不准确,则引发下溢并发出不准确的异常信号。)
检查“下溢”位的浮点状态字,或捕获下溢浮点异常信号。看:
http://www.gnu.org/software/libc/manual/html_node/FP-Exceptions.html
template <typename T>
class Real
{
public:
Real(T x) : x_(x) { }
Real& operator/=(T rhs)
{
if (x_)
{
x_ /= rhs;
if (!x_)
do whatever you want for underflow...
}
}
friend Real operator/(Real lhs, Real rhs)
{ return lhs /= rhs; }
// similar for -= etc.
private:
T x_;
}
首先,您需要在构建中启用浮点异常的生成。其次,您必须在代码中捕获它们。
我做了这样的事情(使用Visual Studio)
void translateFPException( unsigned int u, EXCEPTION_POINTERS* pExp )
{
unsigned int fpuStatus = _clearfp(); // clear the exception
switch (u)
{
case STATUS_FLOAT_DENORMAL_OPERAND:
throw fe_denormal_operand(fpuStatus);
case STATUS_FLOAT_DIVIDE_BY_ZERO:
throw fe_divide_by_zero(fpuStatus);
case STATUS_FLOAT_INEXACT_RESULT:
throw fe_inexact_result(fpuStatus);
case STATUS_FLOAT_INVALID_OPERATION:
throw fe_invalid_operation(fpuStatus);
case STATUS_FLOAT_OVERFLOW:
throw fe_overflow(fpuStatus);
case STATUS_FLOAT_UNDERFLOW:
throw fe_underflow(fpuStatus);
case STATUS_FLOAT_STACK_CHECK:
throw fe_stack_check(fpuStatus);
default:
throw float_exception(fpuStatus);
};
}
void initializeFloatingPointExceptionHandling()
{
unsigned int fpControlWord = 0x00;
_clearfp(); // always call _clearfp before enabling/unmasking a FPU exception
// enabling an exception is done by clearing the respective bit in the control word
errno_t success = _controlfp_s( &fpControlWord,
0xffffffff^( _EM_INVALID
| _EM_ZERODIVIDE
| _EM_OVERFLOW
| _EM_DENORMAL
| _EM_UNDERFLOW
| _EM_INEXACT), _MCW_EM );
if (success != 0)
{
stringstream errStream;
errStream << success << " " << __FILE__ << ":" << __LINE__ << std::endl;
throw exception(errStream.str().c_str());
}
_se_translator_function old =_set_se_translator(translateFPException);
}
void enableAllFPUExceptions()
{
unsigned int oldMask = 0x00000000;
_clearfp();
// enabling is done by clearing the respective bit in the mask
_controlfp_s(&oldMask, 0x00, _MCW_EM);
}
void disableAllFPUExceptions()
{
unsigned int oldMask = 0x00000000;
_clearfp();
// disabling is done by setting the respective bit in the mask
_controlfp_s(&oldMask, 0xffffffff, _MCW_EM);
}
然后您必须编写自己的异常(也只是摘录,但您应该了解概念)
class float_exception : public exception
{
public:
float_exception(unsigned int fpuStatus);
unsigned int getStatus();
protected:
unsigned int fpuStatus;
};
class fe_denormal_operand : public float_exception
{
public:
fe_denormal_operand(unsigned int fpuStatus);
};