为什么不产生双重下溢?

时间:2016-05-05 08:57:10

标签: c++ double precision

任何人都可以解释为什么这个代码段不会产生下溢异常(在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;
}

2 个答案:

答案 0 :(得分:5)

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

  • 浮点异常不是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 ++在20世纪90年代将其作为语言扩展 功能

答案 1 :(得分:3)

假设您使用的是C ++ 11或更高版本,则可以使用fetestexcept函数测试浮点异常。通过将FE_UNDERFLOW常量传递给函数来测试下溢异常。