如何在C语言中检测浮点下溢

How to detect floating point underflow in C

本文关键字:检测 下溢 语言      更新时间:2023-10-16

我需要帮助和示例代码,了解如何以标准方式检测浮点下溢,而不使用第三方库以及有符号和无符号的异常。我在谷歌上搜索了一下,发现许多作者都在谈论"逐渐的底流算不算是底流"?谁能给我解释一下什么是渐下?我需要检查两种情况

感谢您的时间和帮助

看一下"逐渐下沉"的搜索结果并没有给出一个清晰直接的答案,所以:

IEEE-754二进制浮点数在其大部分范围内具有规则模式:有一个符号,一个指数和一个具有一定位数的有效位数(32位float为24,64位double为53)。然而,图案必须在末端被打断。在高端,对于最大指数来说太大的结果将被更改为无穷大。在低端,我们有一个选择。

一种选择是,如果结果低于最低指数,则结果四舍五入为零。然而,IEEE-754使用了一种不同的方案,称为逐渐下流。最低指数的保留格式与常规指数的保留格式不同。

对于正常指数,24位有效位数为"1"。,后面是在有效位域中编码的23位。当数字为次正规时,指数与最低正规指数的值相同,但24位有效位数为"0"。,后面跟着23位。这是渐进的下溢,因为随着数字越来越小,在达到零之前,它们的精度越来越低(有效位数的前导位越来越多为零)。

逐渐下溢具有一些很好的数学性质,特别是a-b == 0当且仅当a == b。由于a-b太小,无法用浮点格式表示,所以即使ab不同,也可能出现a-b == 0。随着逐渐溢出,a-b的所有可能值,对于较小的ab,都是可表示的,因为它们只是指数最低的有效数的差异。

确定是否发生浮点数下溢的另一个问题是,实现允许(根据IEEE-754标准)基于舍入之前或之后的测试报告下溢。在计算结果时,浮点实现必须有效地执行以下步骤:

  • 计算精确结果的符号、指数和有效数。
  • 将结果四舍五入以适应浮点格式。如果有效数四舍五入,这可能会增加指数。

该标准允许使用以下两种方法报告下溢:

  • 计算精确结果的符号、指数和有效数。
  • 指数是否小于正常范围?
  • 将结果四舍五入以适应浮点格式。如果有效数四舍五入,这可能会增加指数。

或:

  • 计算精确结果的符号、指数和有效数。
  • 将结果四舍五入以适应浮点格式。如果有效数四舍五入,这可能会增加指数。
  • 指数是否小于正常范围?

因此,对于相同的计算,两个不同的浮点数实现可能返回不同的关于下溢的报告。

有一些关于处理下溢的附加规则。上述操作导致发出下流异常信号。但是,如果此异常的陷阱未启用并且结果是精确的[舍入没有改变任何东西],则忽略"underflow",并且不会引发underflow标志。如果结果不精确,则引发下流并发出不精确异常。)

检查浮点状态字是否有'underflow'位,或者捕获下流浮点异常信号。看到:

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);
};