c++ - 为什么这不会产生双下溢?

标签 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;
}

最佳答案

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

  • 浮点异常不是 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 年代将其作为语言扩展

关于c++ - 为什么这不会产生双下溢?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37046393/

相关文章:

c++ - Qt 5 中源代码外构建的链接器错误

c++ - 如何使用指针从不同的函数访问局部变量?

java - parseDouble 与 nextDouble

java - 为什么不能将 Byte 对象/字节值转换为 Double 对象?从 Byte 到 Double 的转换会影响精度吗?

wpf - 巨大的 WPF 视觉效果的不精确渲染 - 任何解决方案?

c++ - 如何在 C/C++ 中处理大指数的数字?

scala - 为什么 0.29999999999999998 转换为 0.3?

c++ - 单个生产者和多个单线程消费者

c++ - 返回同类型成员函数指针的成员函数指针

c++ - 将两个 float 相除并不能给出准确的结果