How do I force the C# compiler to throw an excepti

2020-02-12 06:19发布

问题:

Some math functions in a program I recently wrote are returning unacceptable values, such as NaN (possibly due to not checking some function's input params). The problem is that it's being quite difficult to track down which functions are passing the wrong values. This results in errors being propagated throughout the code and making the program crash minutes or hours later, if at all.

I wonder if there's a way to catch these faulty operations the moment a NaN value results from any operation (pretty much the same as in the 'DivisionByZero exception' thrown by some C/C++ compilers, if I remember).

Thanks in advance.

P.D: Please feel free to re-tag my question if needed.

回答1:

Without seeing your code this answer is going to be necessarily vague, but one way of doing it is to check the output from your function and if it's "NaN" raise and exception:

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

But with more details about the exception.

UPDATE

To trap where a specific exception is being thrown you could (temporarily) break when the exception is thrown in the debugger.

Select Debug > Exceptions then expand the tree to select Common Language Runtime Exceptions > System > System.ArithmeticException and check the "Thrown" option.

The problem with this is that it will break everywhere this is thrown, not just in your code. Putting explicit code at a low enough level gets around this.



回答2:

This question seems to be a little older, but since I stumbled about the same problem: Alexander Torstling's answer and the comments below work actually well for me.

What's nice is that even though c# does not provide it's own way for enabling floating point exceptions, it can still catch them (for c++ you need a conversion first).

C#-code is here:

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

The exception I get is {"Overflow or underflow in the arithmetic operation."} System.Exception {System.ArithmeticException}

Not sure why the debugger complains about the signature of _control87; anybody who can improve on that? "Continue" works fine for me, though.



回答3:

I don't know if this works on the CLR, but you can use _controlfp_s from to trigger floating point exceptions:

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

To reset:

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


回答4:

Do you mean you are looking for some setting or option so that as soon as any int gets assigned the value NaN you want an exception to throw? I'm pretty sure no such thing exists. There is the checked option which will warn you about overflows, but thats not the same thing.

I think the only alternative to debugging by hand is to modify the code in the way ChrisF suggested. You could always put an #if DEBUG around the throw to stop if throwing in production code.



回答5:

You could create a class that defines the same operations as an int (or a double), which wraps the int (or double). This class would check for NaN after every operation (N.B. it will be much slower than a simple int or double).

In your code, you would then use this new class everywhere there is an int (or double, respectively). You could even use a template type TIntegerType in your code to decide if you want an int or your class SafeInt.

There may be some who wouldn't like this approach, but I've used it with decent success for some problems (e.g. using high precision math only on problems that need it and using machine precision otherwise).



回答6:

You could always try a conditional break when in debug mode, following is the link to the full answer as I answered someone else with a similar question:

Conditional Debug

This will allow you to simply pause execution when the conditions are met, it may not be an exception but it should help you nonetheless.