2010-02-12 5 views
5

Некоторые математические функции в программе, которую я недавно написал, возвращают неприемлемые значения, такие как NaN (возможно, из-за того, что не проверяются входные параметры некоторых функций). Проблема в том, что довольно сложно отслеживать, какие функции передают неправильные значения. Это приводит к тому, что ошибки распространяются по всему коду и приводят к сбою программы в минутах или через несколько часов, если это вообще возможно.Как заставить компилятор C# генерировать исключение, когда любая математическая операция создает «NaN»?

Интересно, есть ли способ поймать эти неисправные операции в момент, когда значение NaN возникает из-за какой-либо операции (почти так же, как в случае исключения DivisionByZero, созданного некоторыми компиляторами C/C++, если я помню).

Заранее спасибо.

P.D: Пожалуйста, не стесняйтесь повторно пометить мой вопрос, если необходимо.

+0

В чем причина аварии? –

+0

Мне кажется, что это случай, когда следует учитывать включение в разработку. Если ваши математические функции не слишком велики, вы можете правильно определить предварительные и постконфиденциальные ситуации и написать тесты для них, чтобы избежать их возвращения NaN в первую очередь. – Frank

+0

В этом случае доступ к массиву, где index = (long) NaNvalue, но это могло быть что-то еще. – Trap

ответ

9

Не видя кода этот ответ собирается быть обязательно расплывчато, но один из способов сделать это, чтобы проверить выход из вашей функции, и если это «NaN» поднять и исключение:

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

Но подробнее об исключении.

UPDATE

в ловушку, где конкретное исключение бросают вы можете (временно) перерыв, когда исключение в отладчике.

Выберите Отладка> Исключения затем разверните дерево, чтобы выбрать Исключения общего языка Runtime Exceptions> System> System.ArithmeticException и установите флажок «Брошенный».

Проблема в том, что он будет ломаться повсюду, это бросается не только в ваш код. Поместите явный код на достаточно низком уровне обойдется.

+0

Что я хочу сделать, это поймать, где производится значение NaN, без необходимости отладки всего приложения и записывать чек, подобный вашему, на каждый метод, который принимает парные разряды. – Trap

+0

+1 Это именно то, что я искал, большое спасибо за обновление :) – Trap

+0

У меня такая же проблема, но значение, которое меня прослушивает, - это не NaN, а «Бесконечность». Я проверил System.ArithmeticException, как описано выше, но он не срабатывает, когда моя varibale установлена ​​на «Infinity». Есть ли другое исключение, чтобы проверить вариант «Бросок»? – Aaginor

3

Вы имеете в виду, что ищете какую-либо настройку или вариант, чтобы как только любое int получило значение NaN, вы хотите, чтобы исключение выбрало? Я почти уверен, что такого не существует. Существует опция checked, которая предупредит вас о переполнениях, но это не то же самое.

Я думаю, что единственной альтернативой отладке вручную является изменение кода так, как предлагал ChrisF. Вы всегда можете поставить #if DEBUG вокруг броска, чтобы остановить, если бросить производственный код.

+0

Да, более конкретно, когда любой тип значения, а не только int, получает назначение с помощью NaN. – Trap

5

Я не знаю, если это работает на CLR, но вы можете использовать _controlfp_s от запуска исключения с плавающей запятой:

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

Для сброса:

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

Вы связали страницу справки CRT (C runtime library), а внизу указали, что это не относится к среде выполнения .NET. OTOH, он упоминает, что вы можете использовать P/Invoke для вызова функции C. – Lucas

+0

Я не являюсь пользователем .NET, но представляется разумным предположить, что C# использует обычные инструкции с плавающей запятой, и это должно быть поймано. Если C# не связывается с этими флагами, _NAME_NVALID должен запускаться для NaN, и если маска выключена, вы должны получить исключение FP. –

+0

Я также знаю, что эти исключения по умолчанию отключены в программах VC++, поэтому это может стоить того. –

0

Вы всегда можете попробовать условный разрыв в режиме отладки, следующая ссылка на полный ответ, так как я ответил кому-то еще с похожим вопросом:

Conditional Debug

Это позволит вам просто приостановить выполнение при выполнении условий, это может быть не исключение, но оно должно помочь вам, тем не менее.

7

Этот вопрос кажется немного старше, но так как я наткнулся на ту же проблему: Ответ Александра Торстлинга и комментарии ниже работают на самом деле хорошо для меня.

Хорошо, что даже если C# не предоставляет собственный способ включения исключений с плавающей запятой, он все равно может их поймать (для C++ вам нужно сначала преобразование).

C# -кода здесь:

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

Исключение я получаю { "перелива или опустошения в арифметической операции."} {System.Exception System.ArithmeticException}

Не знаю, почему отладчик жалуется на подпись _control87; кто может улучшить это? «Продолжить» отлично работает для меня.

1

Вы можете создать класс, который определяет те же операции, что и int (или double), который обертывает int (или double). Этот класс будет проверять NaN после каждой операции (N.B. будет намного медленнее, чем простой int или double).

В вашем коде вы использовали бы этот новый класс везде, где есть int (или double, соответственно). Вы даже можете использовать тип шаблона TIntegerType в своем коде, чтобы решить, хотите ли вы int или ваш класс SafeInt.

Могут быть некоторые, которым не понравился бы такой подход, но я использовал его с приличным успехом для некоторых проблем (например, используя математику с высокой точностью только для тех задач, которые ей нужны, и в противном случае с использованием точности машины).

Смежные вопросы