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

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

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

发现了问题。 修改调用以使用_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约定调用您的方法。 然后它工作。 下次请写完整的exception……完整的消息应该是:

对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位