Как заставить компилятор C # генерировать исключение, когда любая математическая операция выдает «NaN»? - PullRequest
5 голосов
/ 12 февраля 2010

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

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

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

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

Ответы [ 6 ]

9 голосов
/ 12 февраля 2010

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

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

Но с более подробной информацией об исключении.

UPDATE

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

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

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

7 голосов
/ 25 июля 2013

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

Приятно то, что, хотя 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; Кто-нибудь, кто может улучшить это? «Продолжить» у меня отлично работает.

5 голосов
/ 12 февраля 2010

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

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);
3 голосов
/ 12 февраля 2010

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

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

1 голос
/ 05 ноября 2013

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

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

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

0 голосов
/ 12 февраля 2010

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

Условная отладка

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...