C# 当任何数学运算产生“NaN”时,如何强制C编译器抛出异常?
我最近编写的程序中的一些数学函数返回了不可接受的值,例如NaN,可能是因为没有检查某些函数的输入参数。问题是很难追踪哪些函数传递了错误的值。这会导致错误在整个代码中传播,并使程序在数分钟或数小时后崩溃(如果有的话) 如果我记得的话,我想知道是否有一种方法可以在任何操作产生NaN值的那一刻捕获这些错误操作,这与某些C/C++编译器抛出的“DivisionByZero异常”中的情况几乎相同 提前谢谢C# 当任何数学运算产生“NaN”时,如何强制C编译器抛出异常?,c#,exception,math,C#,Exception,Math,我最近编写的程序中的一些数学函数返回了不可接受的值,例如NaN,可能是因为没有检查某些函数的输入参数。问题是很难追踪哪些函数传递了错误的值。这会导致错误在整个代码中传播,并使程序在数分钟或数小时后崩溃(如果有的话) 如果我记得的话,我想知道是否有一种方法可以在任何操作产生NaN值的那一刻捕获这些错误操作,这与某些C/C++编译器抛出的“DivisionByZero异常”中的情况几乎相同 提前谢谢 p.D:如果需要,请随时重新标记我的问题。在没有看到您的代码的情况下,这个答案必然是模糊的,但一种方
p.D:如果需要,请随时重新标记我的问题。在没有看到您的代码的情况下,这个答案必然是模糊的,但一种方法是检查您的函数的输出,如果是NaN raise和exception:
if (double.IsNaN(result))
{
throw new ArithmeticException();
}
但有更多关于例外的细节
更新
要捕获抛出特定异常的位置,可以在调试器中抛出异常时临时中断
选择Debug>Exceptions,然后展开树以选择Common Language Runtime Exceptions>System>System.ArrithmeticException并选中抛出选项
问题是它会在抛出的任何地方中断,而不仅仅是在代码中。将显式代码放在足够低的级别可以解决这个问题。您的意思是您正在寻找一些设置或选项,以便在任何int被赋值为NaN时,您希望抛出一个异常?我很确定不存在这样的事情。有一个选中的选项将警告您溢出,但这不是同一件事
我认为手工调试的唯一替代方法是按照ChrisF建议的方式修改代码。如果抛出生产代码,您可以在抛出前后放置一个if调试来停止。我不知道这在CLR上是否有效,但您可以使用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);
在调试模式下,您始终可以尝试条件中断,下面是指向完整答案的链接,因为我用类似的问题回答了其他人:
这将允许您在满足条件时简单地暂停执行,这可能不是一个例外,但它应该对您有所帮助。这个问题似乎有点老,但由于我遇到了相同的问题: Alexander Torstling的回答和下面的评论实际上对我很有用
什么是好的,即使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.arithmetricexception}
不确定调试器为什么抱怨_control87的签名;谁能在这方面有所改进?
不过,Continue对我来说很好。您可以创建一个类,定义与int或double相同的操作,它包装int或double。此类将在每次操作N.B.之后检查NaN。它将比简单的int或double慢得多 在您的代码中,您将在分别有int或double的任何地方使用这个新类。您甚至可以在代码中使用模板类型TIntegerType来决定是要使用int还是类SafeInt
可能有人不喜欢这种方法,但我已经在一些问题上成功地使用了这种方法,例如,仅在需要的问题上使用高精度数学,否则使用机器精度。在我看来,这是一种应该考虑将测试纳入开发的情况。如果您的数学函数不太多,您可能希望正确定义前置条件和后置条件,并为它们编写测试,以避免它们首先返回NaN。在这种情况下,访问的数组的index=longNaNvalue,但可能是其他任何内容。@Frank是的,我应该这样做,但我没有,这就是为什么我在这里问:我想做的是捕获NaN值产生的位置,而不必调试整个应用程序,并在每个接受双精度的方法上写一个像你一样的检查。+1这正是我想要的,非常感谢更新:我有同样的问题,但困扰我的不是NaN而是“无限”。如上所述,我检查了System.ArtihmeticException,但当我的varibale设置为“Infinity”时,它不会触发。检查抛出的选项是否还有其他异常?@Aaginor-您想要类似methodLike的陷阱,我想要捕捉无限值产生的时间,了解代码中有问题的部分在哪里能够修复它-无需逐行查找它在哪里。是的,更具体地说,当任何值类型,不仅是int
,被指定为NaN。您链接了C运行时库帮助页,底部的是表示这不适用于.NET运行时。OTOH,它确实提到可以使用P/Invoke来调用C函数。我不是.NET用户,但假设C使用普通浮点指令似乎是合理的,然后应该捕捉到这一点。如果C没有弄乱这些标志,NaN应该触发无效,如果掩码关闭,你应该得到一个FP异常。我也知道这些异常在VC++程序中默认关闭,所以值得一试。谢谢,但这似乎对我没有影响。