谁能解释为什么此代码段不产生下溢异常(在MSVC 2013和gcc @ coliru上)?从平均值函数返回的值小于DBL_MIN

#include <float.h>
#include <iostream>
#include <iomanip>
#include <limits>

const size_t g_testValueCount = 10;
const double g_testValues[g_testValueCount] = { DBL_MIN, 0 };

double unsafeAverage(const double* testValues, size_t testValueCount)
{
    double result = 0;
    for (size_t testValueIndex = 0; testValueIndex < testValueCount; ++testValueIndex)
    {
        result += testValues[testValueIndex];
    }
    return result / testValueCount;
}

int main(int argc, char** argv)
{
    std::cout << "DBL_MIN = " << std::setprecision(std::numeric_limits<double>::digits10) << DBL_MIN << std::endl;
    try
    {
        std::cout << "    AVG = " << std::setprecision(std::numeric_limits<double>::digits10) << unsafeAverage(g_testValues, g_testValueCount) << std::endl;
    }
    catch (...)
    {
        std::cout << "unsafeAverage caught an exception!" << std::endl;
    }
    return 0;
}

最佳答案

您没有捕获到下溢异常的两个主要原因:

  • 浮点异常不是C++异常,因此“一般来说,您不能使用catch(...)捕获它们。
  • MSVC(以及Coliru上的g++)默认的浮点下溢行为是产生一个非正规值或零。异常值是低于普通最小值的值,有效位数较少。当有效位数变为零时,您将得到实际的零。


  • 使用C++ 11和更高版本,您可以通过C99 fetestexcept 函数检查浮点错误。

    这是您的代码经过重写以使用此类检查:
    #include <float.h>
    #include <iostream>
    #include <iomanip>
    #include <limits>
    #include <limits.h>
    using namespace std;
    
    #include <fenv.h>
    
    const size_t g_testValueCount = 10;
    const double g_testValues[g_testValueCount] = { DBL_MIN, 0 };
    
    auto unsafeAverage( const double* const testValues, int const testValueCount )
        -> double
    {
        double result = 0;
        for( int i = 0; i < testValueCount; ++i )
        {
            result += testValues[i];
        }
        return result / testValueCount;
    }
    
    auto main() -> int
    {
        cout  << setprecision( numeric_limits<double>::digits10 );
        cout << "DBL_MIN = " << DBL_MIN << endl;
        try
        {
            feclearexcept( FE_ALL_EXCEPT );
            auto const result = unsafeAverage(g_testValues, g_testValueCount);
            if( fetestexcept(FE_ALL_EXCEPT ) )
            {
                throw std::runtime_error( "Oopsie daisy!" );
            }
            cout << "    AVG = " << result << endl;
        }
        catch( ... )
        {
            cerr << "!unsafeAverage caught an exception" << endl;
            return EXIT_FAILURE;
        }
    }
    

    笔记:
    ¹尽管Visual C++在1990年代将其作为语言扩展

    10-08 05:02