2010-02-12 13 views
5

Alcune funzioni matematiche in un programma che ho scritto di recente stanno restituendo valori inaccettabili, come NaN (probabilmente a causa del mancato controllo dei parametri di input di alcune funzioni). Il problema è che è abbastanza difficile rintracciare quali funzioni stanno trasmettendo i valori sbagliati. Ciò si traduce in propagazione degli errori nel codice e nel fare in modo che il programma si arresti in modo anomalo per alcuni minuti o più tardi.Come faccio a forzare il compilatore C# a generare un'eccezione quando qualsiasi operazione matematica produce "NaN"?

Mi chiedo se ci sia un modo per catturare queste operazioni difettose nel momento in cui un valore NaN risulta da qualsiasi operazione (pressoché lo stesso come nell'eccezione DivisionByZero lanciata da alcuni compilatori C/C++, se ricordo).

Grazie in anticipo.

P.D: Sentitevi liberi di ri-taggare la mia domanda se necessario.

+0

Che cosa sta causando l'arresto anomalo? –

+0

Mi sembra che questo sia un caso in cui si dovrebbe prendere in considerazione l'incorporazione di test nello sviluppo. Se le tue funzioni matematiche non sono troppe, potresti voler definire correttamente le pre e postcondizioni e scrivere test per evitare che restituiscano NaN in primo luogo. – Frank

+0

In questo caso, accedere a un array in cui index = (long) NaNvalue, ma potrebbe essere stato qualsiasi altra cosa. – Trap

risposta

9

Senza vedere il codice di questa risposta sarà necessariamente vago, ma un modo di farlo è quello di verificare l'output della funzione e se è "nan" alzare ed eccezioni:

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

Ma con maggiori dettagli sull'eccezione.

UPDATE

Per intercettare in cui viene gettata una deroga specifica si potrebbe (temporaneamente) rompersi quando viene generata l'eccezione nel debugger.

Selezionare Debug> Eccezioni quindi espandere la struttura per selezionare Eccezioni di runtime in linguaggio comune> Sistema> System.ArithmeticException e selezionare l'opzione "Gettato".

Il problema con questo è che si romperà ovunque questo viene generato, non solo nel codice. Mettere il codice esplicito a un livello sufficientemente basso aggira questo.

+0

Quello che voglio fare è catturare dove viene prodotto il valore NaN senza dover eseguire il debug dell'intera applicazione e scrivere un assegno come il tuo su ogni metodo che accetta il doppio. – Trap

+0

+1 Questo è esattamente quello che stavo cercando, grazie mille per l'aggiornamento :) – Trap

+0

Ho lo stesso problema, ma il valore che mi infastidisce non è NaN ma 'Infinity'. Ho controllato System.ArithmeticException come descritto sopra, ma non si attiva, quando il mio varibale è impostato su 'Infinity'. Esiste un'altra eccezione per controllare l'opzione Gettato? – Aaginor

3

Vuoi dire che stai cercando qualche impostazione o opzione in modo che non appena uno qualsiasi int viene assegnato il valore NaN si desidera un'eccezione da lanciare? Sono abbastanza sicuro che nessuna cosa del genere esiste. C'è l'opzione checked che ti avviserà degli overflow, ma non è la stessa cosa.

Penso che l'unica alternativa al debug a mano è modificare il codice nel modo suggerito da ChrisF. Si può sempre mettere un #if DEBUG intorno al tiro per fermarsi se si lancia il codice di produzione.

+0

Sì, in particolare quando un qualsiasi tipo di valore, non solo int, viene assegnato con NaN. – Trap

5

Non so se questo funziona su CLR, ma è possibile utilizzare _controlfp_s da per attivare eccezioni in virgola mobile:

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

Per ripristinare:

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

La pagina di aiuto CRT (la libreria di runtime C) è collegata e in basso indica che questo non si applica al runtime .NET. OTOH, menziona che è possibile utilizzare P/Invoke per chiamare la funzione C. – Lucas

+0

Non sono un utente .NET, ma sembra ragionevole supporre che C# utilizzi le normali istruzioni in virgola mobile, e questo dovrebbe essere catturato. Se C# non ha problemi con questi flag, _EM_INVALID dovrebbe essere attivato per i NaN, e se la maschera è spenta dovresti ottenere un'eccezione FP. –

+0

So anche che queste eccezioni sono disattivate di default nei programmi VC++, quindi potrebbe valerne la pena. –

0

Si può sempre provare un interruzione condizionale in modalità debug, il seguente è il collegamento alla risposta completa come ho risposto a qualcun altro con una domanda simile:

Conditional Debug

Ciò consentirà di sospendere l'esecuzione quando le condizioni sono soddisfatte, potrebbe non essere un'eccezione ma dovrebbe comunque essere d'aiuto.

7

Questa domanda sembra essere un po 'più vecchia, ma poiché ho inciampato sullo stesso problema: La risposta di Alexander Torstling e i commenti qui sotto funzionano davvero bene per me.

Ciò che è bello è che anche se C# non fornisce il proprio modo per abilitare le eccezioni in virgola mobile, può comunque catturarle (per C++ è necessario prima una conversione).

C# -Code è qui:

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()); 
    } 
    } 
} 

L'eccezione che ottengo è { "Overflow o underflow nella operazione aritmetica."} {System.Exception System.ArithmeticException}

Non certo perché la il debugger si lamenta della firma di _control87; qualcuno che può migliorare su quello? "Continua" funziona bene per me, però.

1

È possibile creare una classe che definisce le stesse operazioni di un int (o di un doppio), che racchiude l'int (o il doppio). Questa classe controllerebbe il NaN dopo ogni operazione (N.B sarà molto più lento di un semplice int o doppio).

Nel tuo codice, dovresti usare questa nuova classe ovunque ci sia un int (o doppio, rispettivamente). Puoi anche usare un modello tipo TIntegerType nel tuo codice per decidere se vuoi un interno o la tua classe SafeInt.

Ci potrebbe essere qualcuno a cui non piacerebbe questo approccio, ma l'ho usato con discreto successo per alcuni problemi (ad esempio usando matematica ad alta precisione solo su problemi che ne hanno bisogno e usando la precisione della macchina altrimenti).

Problemi correlati