c++ performance exception-handling mingw

C++: Catch a divide by by error



performance exception-handling (9)

Aquí hay una pieza simple de código donde ocurre una división por cero. Estoy tratando de atraparlo:

#include <iostream> int main(int argc, char *argv[]) { int Dividend = 10; int Divisor = 0; try { std::cout << Dividend / Divisor; } catch(...) { std::cout << "Error."; } return 0; }

Pero la aplicación falla de todos modos (aunque puse la opción -fexceptions de MinGW ).

¿Es posible atrapar tal excepción (que entiendo que no es una excepción de C ++, sino una excepción de FPU)?

Soy consciente de que podría verificar el divisor antes de dividirlo, pero asumí que, debido a que una división por cero es rara (al menos en mi aplicación), sería más eficiente intentar dividir (y capturar el error si ocurre) que prueba cada vez el divisor antes de dividir.

Estoy haciendo estas pruebas en una computadora con Windows XP, pero me gustaría hacerlo en una plataforma cruzada.


  1. No hay una forma estándar de lenguaje de capturar la división por cero de la CPU.

  2. No "optimice" prematuramente una rama. ¿Está su aplicación realmente ligada a la CPU en este contexto? Lo dudo, y no es realmente una optimización si rompes tu código. De lo contrario, podría hacer que tu código sea aún más rápido:

    int main(int argc, char *argv[]) { /* Fastest program ever! */ }


Bueno, si hubo un manejo de excepción sobre esto, algunos componentes realmente necesitaban hacer el control. Por lo tanto, no pierde nada si lo comprueba usted mismo. Y no hay mucho más rápido que una simple declaración de comparación (una sola instrucción de CPU "saltar si es igual a cero" o algo así, no recuerdo el nombre)


Como otros dijeron, no es una excepción, solo genera un NaN o Inf.

Zero-divide es solo una forma de hacerlo. Si haces muchas matemáticas, hay muchas maneras, como
log(not_positive_number) , exp(big_number) , etc.

Si puede verificar argumentos válidos antes de hacer el cálculo, hágalo, pero a veces es difícil de hacer, por lo que puede necesitar generar y manejar una excepción.

En MSVC hay un archivo de encabezado #include <float.h> contiene una función _finite(x) que indica si un número es finito. Estoy bastante seguro de que MinGW tiene algo similar. Puede probar eso después de un cálculo y lanzar / atrapar su propia excepción o lo que sea.


De alguna manera, la verdadera explicación aún falta.

¿Es posible atrapar tal excepción (que entiendo que no es una excepción de C ++, sino una excepción de FPU)?

Sí, su bloque catch debería funcionar en algunos compiladores. Pero el problema es que su excepción no es una excepción de FPU . Estás haciendo una división entera. No sé si también es un error atrapable, pero no es una excepción FPU, que utiliza una característica de la representación IEEE de números de coma flotante.


Dividir por cero es un error lógico, un error del programador. No debes tratar de sobrellevarlo, debes depurarlo y eliminarlo. Además, capturar excepciones es extremadamente costoso, mucho más que la comprobación de divisores.

Puede utilizar el Manejo estructurado de excepciones para detectar el error dividir por cero. Cómo se logra eso depende de tu compilador. MSVC ofrece una función para capturar Excepciones estructuradas como catch(...) y también proporciona una función para traducir Excepciones estructuradas en excepciones regulares, así como también ofrece __try / __except / __finally . Sin embargo, no estoy lo suficientemente familiarizado con MinGW para decirte cómo hacerlo en ese compilador.


En Windows (con Visual C ++), intente esto:

BOOL SafeDiv(INT32 dividend, INT32 divisor, INT32 *pResult) { __try { *pResult = dividend / divisor; } __except(GetExceptionCode() == EXCEPTION_INT_DIVIDE_BY_ZERO ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { return FALSE; } return TRUE; }

MSDN: http://msdn.microsoft.com/en-us/library/ms681409(v=vs.85).aspx



No es una excepción Es un error que se determina a nivel de hardware y se devuelve al sistema operativo, que luego notifica al programa de alguna manera específica del sistema operativo (como, matando el proceso).

Creo que en tal caso lo que sucede no es una excepción sino una señal. Si es el caso: el sistema operativo interrumpe el flujo de control principal de su programa y llama a un manejador de señal, que, a su vez, interrumpe el funcionamiento de su programa.

Es el mismo tipo de error que aparece cuando desreferencia un puntero nulo (entonces su programa se bloquea por la señal SIGSEGV, falla de segmentación).

Podría tratar de usar las funciones del <csignal> para tratar de proporcionar un controlador personalizado para la señal SIGFPE (es para excepciones de punto flotante, pero puede ser que también se eleve para la división de enteros por cero; estoy realmente inseguro aquí). Sin embargo, debe tener en cuenta que el manejo de la señal depende del sistema operativo y MinGW de alguna manera "emula" las señales POSIX en el entorno de Windows.

Aquí está la prueba en MinGW 4.5, Windows 7:

#include <csignal> #include <iostream> using namespace std; void handler(int a) { cout << "Signal " << a << " here!" << endl; } int main() { signal(SIGFPE, handler); int a = 1/0; }

Salida:

¡Señal 8 aquí!

Y justo después de ejecutar el manejador de señal, el sistema mata el proceso y muestra un mensaje de error.

Al usar esto, puede cerrar cualquier recurso o registrar un error después de una división por cero o una desreferencia de puntero nulo ... pero a diferencia de las excepciones que NO es una forma de controlar el flujo de su programa, incluso en casos excepcionales. Un programa válido no debería hacer eso. Capturar esas señales solo es útil para depurar / diagnosticar.

(Hay algunas señales útiles que son muy útiles en general en la programación de bajo nivel y no hacen que tu programa muera inmediatamente después del controlador, pero ese es un tema profundo).


Para evitar el infinito "¡Señal 8 aquí!" mensajes, solo agrega ''salir'' al código de Kos:

#include <csignal> #include <iostream> #include <cstdlib> // exit using namespace std; void handler(int a) { cout << "Signal " << a << " here!" << endl; exit(1); } int main() { signal(SIGFPE, handler); int a = 1/0; }