2010-02-12 10 views
5

Niektóre funkcje matematyczne w programie, który napisałem ostatnio, zwracają niedopuszczalne wartości, takie jak NaN (prawdopodobnie z powodu braku sprawdzenia niektórych parametrów wejściowych funkcji). Problem polega na tym, że dość trudno jest wyśledzić, które funkcje przekazują błędne wartości. Powoduje to, że błędy są propagowane w całym kodzie i powodują, że program ulega awarii w kilka minut lub kilka godzin później, jeśli w ogóle.Jak wymusić na kompilatorze C# wyrzucenie wyjątku, gdy jakakolwiek operacja matematyczna generuje "NaN"?

Zastanawiam się, czy istnieje sposób na złapanie tych wadliwych operacji w momencie, gdy wartość NaN wynika z jakiejkolwiek operacji (prawie tak samo, jak w "Wyjątku DivisionByZero" wyrzucanym przez niektóre kompilatory C/C++, jeśli pamiętam).

Z góry dziękuję.

P.D: W razie potrzeby możesz ponownie oznaczyć moje pytanie.

+0

Co powoduje awarię? –

+0

Wydaje mi się, że tak jest w przypadku, gdy należy rozważyć włączenie testów do rozwoju. Jeśli twoje funkcje matematyczne nie są zbyt liczne, możesz chcieć poprawnie zdefiniować warunki wstępne i końcowe oraz napisać dla nich testy, aby uniknąć ich powrotu na NaN. – Frank

+0

W tym przypadku dostęp do tablicy, w której indeks = (long) NaNvalue, ale mógł to być cokolwiek innego. – Trap

Odpowiedz

9

Nie widząc swój kod to odpowiedź będzie koniecznie niejasne, ale jednym ze sposobów jest, aby sprawdzić wyjście z funkcji, a jeśli jest „NaN” podnieść i wyjątek:

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

Ale z więcej szczegółów na temat wyjątku.

UPDATE

Aby pułapkę gdzie specyficzny wyjątek jest rzucony można (tymczasowo) break gdy jest wyjątek w debuggera.

Wybierz opcję Debugowanie> Wyjątki, a następnie rozwiń drzewo, aby wybrać Wyjątki dla języka wspólnego Wykonywanie> System> System.ArithmeticException i zaznacz opcję "Zgłoszone".

Problem polega na tym, że będzie on łamał się wszędzie tam, gdzie jest wyrzucany, nie tylko w kodzie. Umieszczanie wyraźnego kodu na wystarczająco niskim poziomie omija to.

+0

Co chcę zrobić, to złapać gdzie wartość NaN jest produkowana bez konieczności debugowania całej aplikacji i pisanie czeku podobnego do twojego na każdej metodzie, która akceptuje podwojenia. – Trap

+0

+1 Właśnie tego szukałem, dziękuję za aktualizację :) – Trap

+0

Mam ten sam problem, ale wartość, która mnie dręczy, nie jest NaN, ale "Nieskończonością". Sprawdziłem wyjątek System.ArithmeticException, jak opisano powyżej, ale nie uruchamia się, gdy mój varibale jest ustawiony na "Infinity". Czy istnieje inny wyjątek, aby sprawdzić opcję wyrzucania? – Aaginor

3

Czy masz na myśli szukanie jakiegoś ustawienia lub opcji, tak aby jakikolwiek int został przypisany do wartości NaN chcesz wyjątek do rzucenia? Jestem prawie pewien, że coś takiego nie istnieje. Istnieje opcja checked, która ostrzeże Cię o przepełnieniu, ale to nie jest to samo.

Myślę, że jedyną alternatywą dla ręcznego debugowania jest zmodyfikowanie kodu w sposób sugerowany przez ChrisF. Zawsze możesz umieścić #if DEBUG wokół rzutu, aby zatrzymać się, jeśli rzucisz kod produkcyjny.

+0

Tak, bardziej szczegółowo, gdy dowolny typ wartości, nie tylko int, zostanie przypisany z NaN. – Trap

5

Nie wiem, czy to działa na CLR, ale można użyć _controlfp_s z wyzwolić pływających wyjątki punkcie:

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

Aby wyzerować:

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

Połączyłeś stronę pomocy CRT (biblioteka środowiska wykonawczego C), a na dole wskazuje, że nie dotyczy to środowiska wykonawczego .NET. OTOH, wspomina, że ​​możesz użyć P/Invoke, aby wywołać funkcję C. – Lucas

+0

Nie jestem użytkownikiem .NET, ale wydaje się rozsądne założenie, że C# używa normalnych instrukcji zmiennoprzecinkowych, a to powinno zostać przechwycone. Jeśli C# nie będzie bałagan z tymi flagami, _EM_INVALID powinno być wyzwalane dla NaN, a jeśli maska ​​jest wyłączona, powinieneś otrzymać wyjątek FP. –

+0

Wiem również, że te wyjątki są domyślnie wyłączone w programach VC++, więc warto byłoby spróbować. –

0

Zawsze można spróbować przerwanie warunkowe w trybie debugowania, poniżej znajduje się link do pełnej odpowiedzi, ponieważ odpowiedziałem na inną osobę z podobnym pytaniem:

Conditional Debug

Umożliwi to po prostu wstrzymanie wykonywania, gdy spełnione są warunki, może nie być wyjątkiem, ale powinno jednak pomóc.

7

To pytanie wydaje się być trochę starsze, ale ponieważ natknąłem się na ten sam problem: Odpowiedź Alexandra Torstlinga i poniższe komentarze działają dla mnie naprawdę dobrze.

Co jest fajne, mimo że C# nie zapewnia własnego sposobu włączania wyjątków zmiennoprzecinkowych, może je jeszcze przechwycić (w języku C++ najpierw trzeba przeprowadzić konwersję).

C# -code jest tutaj:

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

Wyjątek otrzymuję jest { "Przepełnienie lub niedomiar w operacji arytmetycznej."} System.Exception {System.ArithmeticException}

Nie wiem dlaczego debugger narzeka na podpis _control87; ktokolwiek może to poprawić? "Dalej" działa dobrze dla mnie.

1

Można utworzyć klasę, która definiuje te same operacje, co int (lub double), która otacza int (lub double). Ta klasa będzie sprawdzać NaN po każdej operacji (N.B. będzie to znacznie wolniejsze niż proste int lub podwójne).

W twoim kodzie używałbyś tej nowej klasy wszędzie tam, gdzie jest int (lub podwójne, odpowiednio). Możesz nawet użyć szablonu typu TIntegerType w kodzie, aby zdecydować, czy chcesz int, czy klasę SafeInt.

Niektórzy mogą nie lubić tego podejścia, ale użyłem go z przyzwoitym sukcesem w przypadku niektórych problemów (np. Przy użyciu precyzyjnej matematyki tylko w przypadku problemów, które tego wymagają, a także w innych przypadkach z użyciem precyzji maszyny).

Powiązane problemy