我需要有关如何以标准方式检测浮点下溢的帮助和示例代码,而不使用第三方库以及有符号和无符号的例外。我用谷歌搜索,发现不同的作者都在谈论“逐渐下溢或不下溢”?任何人都可以解释一下逐渐下溢的情况吗?我需要检查两种情况
感谢您的时间和帮助
答案 0 :(得分:2)
查看“逐渐下溢”的热门搜索结果并未显示明确而直接的答案,因此:
IEEE-754二进制浮点数在其大部分范围内都有一个规则模式:有一个符号,一个指数和一个有一定数量位的有效数字(32位float
为24, 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标准)实现在舍入之前或之后基于测试报告下溢。在计算结果时,浮点实现有效地必须执行以下步骤:
该标准允许实施报告下溢:
或:
因此,两个不同的浮点实现可能会返回有关同一计算的下溢的不同报告。
(还有一些关于处理下溢的附加规则。上面会导致下溢异常被发出信号。但是,如果没有启用来自此异常的陷阱并且结果是准确的[舍入没有改变任何东西],那么“下溢” “被忽略,并且不会引发下溢标志。如果结果不准确,则引发下溢并发出不准确的异常。”
答案 1 :(得分:1)
检查浮点状态字的“下溢”位,或捕获下溢浮点异常的信号。参见:
http://www.gnu.org/software/libc/manual/html_node/FP-Exceptions.html
答案 2 :(得分:0)
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_;
}
答案 3 :(得分:0)
首先,您需要在构建中启用浮点异常的生成。其次,你必须在你的代码中捕获它们。
我做了类似的事情(使用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);
};