如何检测C中的浮点下溢

时间:2013-03-28 05:50:06

标签: c++ c floating-point underflow

我需要有关如何以标准方式检测浮点下溢的帮助和示例代码,而不使用第三方库以及有符号和无符号的例外。我用谷歌搜索,发现不同的作者都在谈论“逐渐下溢或不下溢”?任何人都可以解释一下逐渐下溢的情况吗?我需要检查两种情况

感谢您的时间和帮助

4 个答案:

答案 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即使ab有所不同,因为a-b太小而无法以浮点格式表示。随着逐渐溢出,a-b的所有可能值(小ab)都是可表示的,因为它们只是具有最低指数的有效数字的差异。

确定是否发生浮点下溢的另一个问题是允许(通过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);
};