簡體   English   中英

為什么不產生雙重下溢?

[英]Why doesn't this produce a double underflow?

任何人都可以解釋為什么這個片段不會產生下溢異常(在MSVC 2013和gcc @coliru上)? 從average函數返回的值低於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 ++在20世紀90年代將其作為語言擴展

假設您使用的是C ++ 11或更高版本,則可以使用fetestexcept函數測試浮點異常。 通過將FE_UNDERFLOW常量傳遞給函數來測試下溢異常。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM