2010-02-12 7 views
5

Algunas funciones matemáticas de un programa que escribí recientemente devuelven valores inaceptables, como NaN (posiblemente debido a que no se verificaron los parámetros de entrada de alguna función). El problema es que es bastante difícil rastrear qué funciones están pasando los valores incorrectos. Esto hace que los errores se propaguen a través del código y que el programa falle minutos u horas más tarde, si es que lo hace.¿Cómo forzo al compilador de C# a lanzar una excepción cuando cualquier operación matemática produce 'NaN'?

Me pregunto si hay una forma de detectar estas operaciones defectuosas en el momento en que NaN resulta de cualquier operación (más o menos lo mismo que en la 'excepción DivisionByZero' lanzada por algunos compiladores C/C++, si mal no recuerdo).

Gracias de antemano.

P.D: Por favor, siéntase libre de volver a etiquetar mi pregunta si es necesario.

+0

¿Qué está causando el bloqueo? –

+0

Me parece que este es un caso donde se debe considerar la incorporación de pruebas en el desarrollo. Si sus funciones matemáticas no son demasiadas, es posible que desee definir correctamente las condiciones previas y posteriores y escribir pruebas para evitar que devuelvan NaN en primer lugar. – Frank

+0

En este caso accediendo a una matriz donde el índice = (largo) NaNvalue, pero podría haber sido cualquier otra cosa. – Trap

Respuesta

9

Sin ver su código esta respuesta va a ser necesariamente vago, pero una forma de hacerlo es comprobar la salida de su función y si es "NaN" subir y excepción:

if (double.IsNaN(result)) 
{ 
    throw new ArithmeticException(); 
} 

Pero con más detalles sobre la excepción.

ACTUALIZACIÓN

Para atrapar donde se produce una excepción específica que podría (temporalmente) romper cuando la excepción en el depurador.

Seleccione Depurar> Excepciones luego expanda el árbol para seleccionar Excepciones comunes de tiempo de ejecución> Sistema> System.ArithmeticException y marque la opción "Lanzada".

El problema con esto es que se romperá en todas partes, no solo en el código. Poniendo el código explícito a un nivel suficientemente bajo se soluciona esto.

+0

Lo que quiero hacer es detectar dónde se produce el valor de NaN sin tener que depurar toda la aplicación y escribir un cheque como el tuyo en cada método que acepte dobles. – Trap

+0

+1 Eso es exactamente lo que estaba buscando, muchas gracias por la actualización :) – Trap

+0

Tengo el mismo problema, pero el valor que me está molestando no es NaN sino 'Infinito'. Comprobé System.ArithmeticException como se describió anteriormente, pero no se dispara, cuando mi varibale está configurado en 'Infinity'. ¿Hay alguna otra excepción para verificar la opción lanzada? – Aaginor

3

¿Quiere decir que está buscando alguna configuración u opción para que tan pronto como se le asigne int al valor NaN, desee una excepción para arrojar? Estoy bastante seguro de que no existe tal cosa. Existe la opción checked que le avisará sobre los desbordamientos, pero no es lo mismo.

Creo que la única alternativa a la depuración manual es modificar el código en la forma sugerida por ChrisF. Siempre puede poner un #if DEBUG alrededor del tiro para detenerlo si arroja el código de producción.

+0

Sí, más específicamente cuando cualquier tipo de valor, no solo int, se asigna con NaN. – Trap

5

No sé si esto funciona en el CLR, pero se puede usar para activar _controlfp_s de excepciones de punto flotante:

unsigned int _oldState; 
errno_t err = _controlfp_s(&oldState, 0, MCW_EM); 
assert(!err); 

Para restablecer:

errno_t err = _controlfp_s(0, _oldState, MCW_EM); 
assert(!err); 
+0

Ha vinculado la página de ayuda de CRT (la biblioteca de tiempo de ejecución C), y en la parte inferior indica que esto no se aplica al tiempo de ejecución de .NET. OTOH, menciona que podría usar P/Invoke para llamar a la función C. – Lucas

+0

No soy un usuario de .NET, pero parece razonable suponer que C# usa instrucciones de coma flotante normales, y esto debería ser capturado. Si C# no causa problemas con estos indicadores, _EM_INVALID debe activarse para los NaN, y si la máscara está desactivada, debería obtener una excepción de FP. –

+0

También sé que estas excepciones están desactivadas por defecto en los programas de VC++, por lo que podría valer la pena intentarlo. –

0

Siempre se puede intentar una salto condicional cuando está en modo de depuración, a continuación se muestra el enlace a la respuesta completa, ya que respondí a otra persona con una pregunta similar:

Conditional Debug

Esto le permitirá simplemente pausar la ejecución cuando se cumplan las condiciones, puede que no sea la excepción, pero debería serle de ayuda.

7

Esta pregunta parece ser un poco más antigua, pero desde que tropecé con el mismo problema: La respuesta de Alexander Torstling y los comentarios a continuación funcionan realmente bien para mí.

Lo que es bueno es que, aunque C# no proporciona su propia forma para habilitar excepciones de coma flotante, aún puede atraparlos (para C++ primero necesita una conversión).

C# -code está aquí:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 
using System.Runtime.InteropServices; 

namespace ConsoleApplication2 
{ 
    class Program 
    { 
    [System.Runtime.InteropServices.DllImport("msvcrt.dll")] 
    public static extern uint _control87(uint a, uint b); 

    [System.Runtime.InteropServices.DllImport("msvcrt.dll")] 
    public static extern uint _clearfp(); 

    static void Main(string[] args) 
    { 
     float zero = 0.0f - args.Length; // Want 0.0f. Fool compiler... 
     System.Console.WriteLine("zero = " + zero.ToString()); 

     // A NaN which does not throw exception 
     float firstNaN = zero/0.0f; 
     System.Console.WriteLine("firstNaN= " + firstNaN.ToString()); 

     // Now turn on floating-point exceptions 
     uint empty = 0; 
     uint cw = _control87(empty, empty); // Debugger halts on this one and complains about false signature, but continue works. 
     System.Console.WriteLine(cw.ToString()); 
     uint MCW_EM = 0x0008001f; // From float.h 
     uint _EM_INVALID = 0x00000010; // From float.h (invalid corresponds to NaN 
     // See http://www.fortran-2000.com/ArnaudRecipes/CompilerTricks.html#x86_FP 

     cw &= ~(_EM_INVALID); 
     _clearfp(); // Clear floating point error word. 
     _control87(cw, MCW_EM); // Debugger halts on this one and complains about false signature, but continue works.  
     System.Console.WriteLine(cw.ToString()); 

     // A NaN which does throw exception 
     float secondNaN = 0; 
     try 
     { 
     // Put as much code here as you like. 
     // Enable "break when an exception is thrown" in the debugger 
     // for system exceptions to get to the line where it is thrown 
     // before catching it below. 
     secondNaN = zero/0.0f; 
     } 
     catch (System.Exception ex) 
     { 
     _clearfp(); // Clear floating point error word. 
     }  

     System.Console.WriteLine("secondNaN= " + secondNaN.ToString()); 
    } 
    } 
} 

La excepción que se ve es { "desbordamiento positivo o negativo en la operación aritmética."} {System.Exception System.ArithmeticException}

No sé por qué la el depurador se queja de la firma de _control87; alguien que puede mejorar en eso? "Continuar" funciona bien para mí, sin embargo.

1

Puede crear una clase que defina las mismas operaciones que un int (o un doble), que envuelve el int (o doble). Esta clase verificaría NaN después de cada operación (N.B. será mucho más lento que una simple int o doble).

En su código, usted usaría esta nueva clase en cualquier lugar donde haya una int (o doble, respectivamente). Incluso podría usar una plantilla tipo TIntegerType en su código para decidir si desea una int o su clase SafeInt.

Puede haber algunos a los que no les gustaría este enfoque, pero lo he usado con éxito para algunos problemas (por ejemplo, usar cálculos matemáticos de alta precisión solo en problemas que lo necesiten y usar la precisión de la máquina).

Cuestiones relacionadas