5

我有一个很长的复杂源代码,我需要找到将变量值设置为 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());
    }
  }
}
4

1 回答 1

3

发现了问题。将调用修改为 use_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替换为。运算符将由隐式转换提供。Singledoublefloat

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

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

于 2013-09-15T10:56:50.893 回答