2010-05-05 8 views
10

estoy usando C++ en Visual Studio Express para generar árboles de expresión al azar para su uso en un tipo de algoritmo genético del programa.¿Cómo uso try ... catch para detectar los errores de coma flotante?

Como son aleatorios, los árboles suelen generar: dividir por cero, desbordamiento, desbordamiento, así como devolver "inf" y otras cadenas. Puedo escribir controladores para las cuerdas, pero la literatura me dejó desconcertado sobre los demás. Si lo entiendo correctamente, ¿tengo que establecer algunas banderas primero?

asesoramiento y/o un puntero a alguna literatura sería apreciada. Editar: los valores devueltos en la variable doble son 1. # INF o -1. # IND. Me equivoqué al llamarlos hilos.

+0

C++ no dicta ninguna de esas operaciones debe lanzar una excepción. Conducen a un comportamiento indefinido. (Que puede bloquearse, lanzar una excepción o no hacer nada, o ...) – GManNickG

+1

Pero C99 y POSIX sí especifican tales cosas y proporcionan una interfaz de excepción numérica. Sin embargo, no está claro si Peter tiene control sobre los números reales, si está obteniendo cadenas en lugar de infinitos FP. – Potatoswatter

Respuesta

6

¿Seguro que desea para su captura en lugar de simplemente ignorarlos? Suponiendo que lo que desea es hacer caso omiso de ellas:

ver esto: http://msdn.microsoft.com/en-us/library/c9676k6h.aspx

Para la máscara _MCW_EM, despejando la máscara establece la excepción, que permite la excepción de hardware; establecer la máscara oculta la excepción.

por lo que vamos a querer hacer algo como esto:

#include <float.h> 
#pragma fenv_access (on) 

void main() 
{ 
    unsigned int fp_control_word; 
    unsigned int new_fp_control_word; 

    _controlfp_s(&fp_control_word, 0, 0); 

    // Make the new fp env same as the old one, 
    // except for the changes we're going to make 
    new_fp_control_word = fp_control_word | _EM_INVALID | _EM_DENORMAL | _EM_ZERODIVIDE | _EM_OVERFLOW | _EM_UNDERFLOW | _EM_INEXACT; 
    //Update the control word with our changes 
    _controlfp_s(&fp_control_word, new_fp_control_word, _MCW_EM) 


} 

Parte de la confusión que aquí puede haber sobre el uso de la palabra "excepción". En C++, eso generalmente se refiere al sistema de manejo de excepciones integrado en el lenguaje. Las excepciones de puntos flotantes son una bestia completamente diferente. Las excepciones que una FPU estándar debe soportar son todas definidas en IEEE-754. Suceden dentro de la unidad de punto flotante, que puede hacer cosas diferentes dependiendo de cómo se configuren los indicadores de control de la unidad de punto flotante. Por lo general, ocurre una de estas dos cosas: 1) Se ignora la excepción y la FPU establece un indicador que indica que se ha producido un error en su (s) registro (s) de estado. 2) La excepción no es ignorado por el FPU, así que en vez de una interrupción se genera, y cualquiera que sea controlador de interrupción se creó para los errores de punto flotante se vuelve a llamar.Por lo general, esto hace algo bueno para ti, como provocar que rompas esa línea de código en el depurador o generes un archivo central.

Puede encontrar más información sobre IEE-754 aquí: http://www.openwatcom.org/ftp/devel/docs/ieee-754.pdf

Algunas referencias adicionales de punto flotante: http://docs.sun.com/source/806-3568/ncg_goldberg.html http://floating-point-gui.de/

+1

Suponiendo que el código se está ejecutando en un procesador x86 ... – dthorpe

+1

dthorpe: en realidad, no estoy asumiendo que se está ejecutando en un procesador x86. Los documentos de MSDN afirman explícitamente que _controlfp_s es la nueva forma de "plataforma independiente" para hacer esto. Sospecho que por plataforma independiente se refieren al tipo de CPU, ya que obviamente no todos los sistemas operativos x86 tienen esta función, siempre y cuando lo haga en una plataforma Windows, debería estar bien. – George

+2

También tenga en cuenta que he visto bibliotecas enfrentando el ajuste de la palabra de control de coma flotante, cada una de las cuales impone su preferencia en la otra. Casi tan malo como que los controladores de impresora cambien la configuración regional bajo la alimentación de un programa ... –

-1

nunca había probado, pero asumiendo división por cero produce una excepción, que sólo podría envolver el código en un try/catch como esta:

tratar {// potencial de división por cero ... } captura (...) {// ... captura todas las excepciones }

+0

Eso está mal, porque el entorno de tiempo de ejecución C++ no lo ayudará aquí. No lanzará una excepción por ningún motivo que no sea el que el programador específicamente solicitó lanzar. – wilhelmtell

1

el entorno de ejecución de C++ no le ayudará el más mínimo aquí. Debe realizar estos controles usted mismo, explícitamente en su código. A menos, por supuesto, que las funciones a las que llama hagan estos controles, en cuyo caso depende de cómo se comporten en caso de error.

Me explico:

double divide(double a, double b) { 
    return a/b; // undefined if b is zero 
} 

Debe ser, de hecho,

double divide(double a, double b) { 
    if(b == 0) { 
     // throw, return, flag, ... you choose how to signal the error 
    } 
    return a/b; // b can't possibly be zero here 
} 

Si el código que falla en la división por cero y tal no es el suyo, entonces tendrás que cavar más profundo para encontrar lo que hace en el caso de una amenaza por un error. ¿Lanza? Establecer una bandera? Pregunta al autor y/o lee la fuente.

He aquí un ejemplo de una excepción:

struct bad_value : public std::exception { }; 

double divide(double a, double b) { 
    if(b == 0) throw bad_value("Division by zero in divide()"); 
    return a/b; // b can't possibly be zero here 
} 

// elsewhere (possibly in a parallel universe) ... 

    try { 
     double r1 = divide(5,4); 
     double r2 = divide(5,0); 
    } catch(bad_value e) { 
     // ... 
    } 
+0

Gracias. Parece que tendré que implementar mis propios "lanzamientos". –

8

Según http://msdn.microsoft.com/en-us/library/aa289157%28v=vs.71%29.aspx#floapoint_topic8,
parece posible lanzar excepciones de C++ en MSVC

Cree las clases de excepciones:

class float_exception : public std::exception {}; 
class fe_denormal_operand : public float_exception {}; 
class fe_divide_by_zero : public float_exception {}; 
class fe_inexact_result : public float_exception {}; 
class fe_invalid_operation : public float_exception {}; 
class fe_overflow : public float_exception {}; 
class fe_stack_check : public float_exception {}; 
class fe_underflow : public float_exception {}; 

Mapa C++ tirar a excepciones FPU usando gestor de excepciones estructurado

void se_fe_trans_func(unsigned int u, EXCEPTION_POINTERS* pExp) 
{ 
    switch (u) 
    { 
    case STATUS_FLOAT_DENORMAL_OPERAND: throw fe_denormal_operand(); 
    case STATUS_FLOAT_DIVIDE_BY_ZERO:  throw fe_divide_by_zero(); 
    etc... 
    }; 
} 
. . . 
_set_se_translator(se_fe_trans_func); 

A continuación, puede utilizar tratar captura

try 
{ 
    floating-point code that might throw divide-by-zero 
    or other floating-point exception 
} 
catch(fe_divide_by_zero) 
{ 
    cout << "fe_divide_by_zero exception detected" << endl; 
} 
catch(float_exception) 
{ 
    cout << "float_exception exception detected" << endl; 
} 
+2

Esta debería ser la respuesta aprobada: corresponde a la pregunta original (la respuesta aceptada básicamente dice "no deberías molestar" y la segunda dice "no puedes" (lo cual es incorrecto). – Deimos