C# 当任何数学运算产生“NaN”时,如何强制C编译器抛出异常?

C# 当任何数学运算产生“NaN”时,如何强制C编译器抛出异常?,c#,exception,math,C#,Exception,Math,我最近编写的程序中的一些数学函数返回了不可接受的值,例如NaN,可能是因为没有检查某些函数的输入参数。问题是很难追踪哪些函数传递了错误的值。这会导致错误在整个代码中传播,并使程序在数分钟或数小时后崩溃(如果有的话) 如果我记得的话,我想知道是否有一种方法可以在任何操作产生NaN值的那一刻捕获这些错误操作,这与某些C/C++编译器抛出的“DivisionByZero异常”中的情况几乎相同 提前谢谢 p.D:如果需要,请随时重新标记我的问题。在没有看到您的代码的情况下,这个答案必然是模糊的,但一种方

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

如果我记得的话,我想知道是否有一种方法可以在任何操作产生NaN值的那一刻捕获这些错误操作,这与某些C/C++编译器抛出的“DivisionByZero异常”中的情况几乎相同

提前谢谢


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++程序中默认关闭,所以值得一试。谢谢,但这似乎对我没有影响。