【问题标题】:How do I force the C# compiler to throw an exception when any math operation produces 'NaN' in .net 4?当任何数学运算在 .net 4 中产生“NaN”时,如何强制 C# 编译器抛出异常?
【发布时间】:2013-09-19 15:01:24
【问题描述】:

我有一个很长很复杂的源代码,我需要找到将变量值设置为 nan 的确切位置。所以我需要编译器在那时抛出异常。 这个问题以前有人问过。我发现以下答案是一个很好的答案。此代码在 .net 3.5 中运行良好。但是当我使用 .net 4 时,此解决方案无法正常工作。即使我在调试器中启用“抛出异常时中断”,也无法在代码中找到异常。有什么想法吗?

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

【问题讨论】:

  • 你的问题是什么...编译时错误或吞下运行时错误?
  • @SimonWhitehead 我会说他想将信号 NaN 捕获为异常。
  • 如果你想捕获所有个浮点异常,改变uint _EM_INVALID = 0x0000001F;。请参阅_controlfp 命令的文档。例如,捕捉Math.Log(0.0) .

标签: c# exception math nan


【解决方案1】:

发现问题。将调用修改为使用_controlfp 而不是_control87,然后将DllImport 更改为:

[System.Runtime.InteropServices.DllImport("msvcrt.dll", CallingConvention = System.Runtime.InteropServices.CallingConvention.Cdecl)]
public static extern uint _controlfp(uint a, uint b);

[System.Runtime.InteropServices.DllImport("msvcrt.dll", CallingConvention = System.Runtime.InteropServices.CallingConvention.Cdecl)]
public static extern uint _clearfp();

您必须明确告诉 .NET,您的方法必须使用 Cdecl 约定调用。然后它工作。下次请写完整的异常...完整的消息应该是:

对 PInvoke 函数“_controlfp”的调用导致堆栈不平衡。这可能是因为托管 PInvoke 签名与非托管目标签名不匹配。检查 PInvoke 签名的调用约定和参数是否与目标非托管签名匹配。

或者你可以作弊:-)

struct DoubleNoNan
{
    public readonly double Value;

    public DoubleNoNan(double value)
    {
        if (Double.IsNaN(value))
        {
            throw new Exception("NaN");
        }

        this.Value = value;
    }

    public static implicit operator double(DoubleNoNan value)
    {
        return value.Value;
    }

    public static implicit operator DoubleNoNan(double value)
    {
        return new DoubleNoNan(value);
    }

    public override bool Equals(object obj)
    {
        return this.Value.Equals(obj);
    }

    public override int GetHashCode()
    {
        return this.Value.GetHashCode();
    }

    public override string ToString()
    {
        return this.Value.ToString();
    }
}

然后:

DoubleNoNan dn = 0.0;
dn = / 0.0;

对于float,将单词Double 替换为Single,将单词double 替换为float。运算符将由隐式转换提供。

显然它有其局限性(它不能用于ref/out 表达式以及许多其他表达式)

您的代码似乎可以正常工作...我已经检查了 32 位和 64 位的代码

【讨论】:

  • 感谢您的回答。您的第一个解决方案解决了一半的问题,但我仍然看到堆栈溢出异常。当代码到达我希望发生 nan 异常的位置时,我看到:“mscorlib.dll 中发生了类型为 'System.StackOverflowException' 的未处理异常”。
  • @Soraya 然后有一个StackOverflow :-) 看调用栈(Debug->Windows->Call Stack)
  • 不幸的是调用堆栈不能帮助我。 “[外部代码]”是我的调用堆栈的内容:D
  • @Soraya 只有“外部代码”?不能向下或向上滚动?
  • 是的。无非是“外部代码”。用您的新代码替换 dll 并找到 nan 异常的确切位置后,代码是否在您的计算机上正常运行?
猜你喜欢
  • 2011-01-16
  • 2011-01-17
  • 1970-01-01
  • 1970-01-01
  • 2016-12-15
  • 2010-12-14
  • 1970-01-01
相关资源
最近更新 更多