当任何数学运算产生’NaN’时,如何强制C#编译器抛出exception?

我最近编写的程序中的一些数学函数返回了不可接受的值,例如NaN(可能是因为没有检查某些函数的输入参数)。 问题是很难找到哪些函数传递了错误的值。 这会导致错误在整个代码中传播,并使程序在几分钟或几小时后崩溃,如果有的话。

我想知道是否有办法在任何操作产生NaN值的时候捕获这些错误操作(与我记得的某些C / C ++编译器抛出的’DivisionByZeroexception’几乎相同)。

提前致谢。

PD:如果需要,请随时重新标记我的问题。

在没有看到你的代码的情况下,这个答案必然是模糊的,但是这样做的一种方法是检查你的函数的输出,如果它是“NaN”加注和exception:

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

但有关exception的更多细节。

UPDATE

要捕获抛出特定exception的位置,您可以(暂时)在调试器中抛出exception时中断。

选择Debug> Exceptions,然后展开树以选择Common Language Runtime Exceptions> System> System.ArithmeticException并选中“Thrown”选项。

这样做的问题在于它会在抛出的任何地方破坏,而不仅仅是在你的代码中。 将显式代码置于足够低的水平可以解决这个问题。

这个问题似乎有点老了,但是因为我偶然发现了同样的问题:亚历山大·托斯特林的回答以及下面的评论对我来说非常有用。

令人高兴的是,即使c#没有提供自己的方式来启用浮点exception,它仍然可以捕获它们(对于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()); } } } 

我得到的exception是{“算术运算中的溢出或下溢。”} System.Exception {System.ArithmeticException}

不知道为什么调试器会抱怨_control87的签名; 谁能改进呢? 不过,“继续”对我来说很好。

我不知道这是否适用于CLR,但您可以使用_controlfp_s from来触发浮点exception:

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

你的意思是你正在寻找一些设置或选项,以便一旦任何int被赋值NaN你想要抛出exception? 我很确定不存在这样的事情。 有一个checked选项会警告你溢出,但那不是一回事。

我认为手动调试的唯一替代方法是以ChrisF建议的方式修改代码。 如果投入生产代码,你总是可以在抛出周围放置一个#if DEBUG来停止。

您可以创建一个类,它定义与int(或double)相同的操作,它包装int(或double)。 这个类会在每次操作后检查NaN(NB会比简单的int或double慢得多)。

在你的代码中,你将在任何地方使用这个新类,分别是int(或double)。 您甚至可以在代码中使用模板类型TIntegerType来决定是否需要int或类SafeInt

可能有些人不喜欢这种方法,但是我已经成功地使用了它来解决一些问题(例如,仅在需要它的问题上使用高精度数学,否则使用机器精度)。

你总是可以在调试模式下尝试条件中断,以下是完整答案的链接,因为我回答了其他有类似问题的人:

条件调试

这将允许您在条件满足时简单地暂停执行,它可能不是例外,但它应该可以帮助您。