2016-05-05 3 views
2

Может ли кто-нибудь объяснить, почему этот фрагмент не создает исключение underflow (на 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; 
} 
+0

Взгляните на эту функцию: http://en.cppreference.com/w/cpp/numeric/fenv/fetestexcept 'fetestexcept' –

+0

@TommyA -thanks, я не знал об этом. Да, флаг исключения для underflow установлен так, что это все объясняет. –

ответ

5

Две основные причины вы не поймать сгущенного исключение:

  • Плавающие исключения точки не являются исключениями C++, так ¹in вообще вы не можете поймать их с catch(...).

  • По умолчанию поведение потока с плавающей запятой по умолчанию с MSVC (и, предположительно, с g ++ на Coliru) - это создание денормального значения или нуля. Денормаль - это значение ниже обычного минимума с меньшим количеством бит. По мере того, как количество значащих бит переходит в ноль, вы получаете фактический ноль.


С 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-е годы

+0

Что касается первого момента - как я могу поймать и обработать исключение underflow? Что касается второго, ну, я ожидал хотя бы дать мне ноль. Причина этого в том, что я хотел сделать «безопасную» среднюю функцию, но теперь я даже не могу заставить исключение иметь плохой пример: D –

+0

Просто из любопытства есть ли настоящая причина, по которой вы хотели бы используйте формат возвращаемого типа возврата для возвращающих примитивов функций? или вы просто пытаетесь усложнить пример больше, чем нужно? :) –

+0

@TommyA: Ваш вопрос не имеет смысла для меня, извините. Использование двух разных синтаксисов явно сложнее, чем использование одного, вопреки вашему предположению/утверждению/независимо. –

3

Предполагая, что вы используя C++ 11 или новее, вы можете протестировать исключения с плавающей запятой, используя функцию fetestexcept. Тестирование для исключений underflow путем передачи константы FE_UNDERFLOW функции.

+0

Хорошая особенность. Таким образом вы можете бросить свои собственные пользовательские исключения и полностью исключить SEH. –

Смежные вопросы