当任何数学运算在.net 4中产生"NaN"时,如何强制C#编译器引发异常? [英] How do I force the C# compiler to throw an exception when any math operation produces 'NaN' in .net 4?
问题描述
我有一个很长的复杂源代码,我需要找到将变量值设置为nan的确切位置.因此,我需要编译器在此时抛出异常. 这个问题已经被问过了.我发现以下答案是一个很好的答案.这段代码在.net 3.5中运行良好,但是当我使用.net 4时,此解决方案无法正常工作.即使在调试器中启用了引发异常时中断",该异常也无法位于代码中.有什么主意吗?
I have a long complicated source code and I need to find the exact location at which a variable value set to nan. So I need the compiler to throw an exception at that point. This question has been asked before. I found the following answer as a good answer. This code works well in .net 3.5.But when I use .net 4, this solution doesn't work correctly. Even when I Enable "break when an exception is thrown" in the debugger, the exception can't be located in the code. Any idea?
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
更改为:
Found the problem. Modify the calls to use _controlfp
instead of _control87
, and then change the DllImport
to:
[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
约定来调用您的方法.然后就可以了.下次,请写完整的例外...完整的消息应该是:
You have to explicitly tell .NET that your methods must be called with the Cdecl
convention. Then it works. Next time please write the full exception... The full message should be:
对PInvoke函数'_controlfp'的调用已使堆栈不平衡.这可能是因为托管PInvoke签名与非托管目标签名不匹配.检查PInvoke签名的调用约定和参数是否与目标非托管签名匹配.
A call to PInvoke function '_controlfp' has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature. Check that the calling convention and parameters of the PInvoke signature match the target unmanaged signature.
或者您可以作弊:-)
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
.运算符将由隐式强制转换提供.
For the float
, replace the word Double
with Single
and the word double
with float
. Operators will be provided by the implicit casts.
显然它有其局限性(不能在ref
/out
表达式以及许多其他表达式中使用)
Clearly it has its limits (it can't be used in ref
/out
expressions, and many others)
您的代码似乎可以正常工作...我已经以32位和64位格式对其进行了检查
Your code seems to work correctly... I've checked it at 32 and 64 bits
这篇关于当任何数学运算在.net 4中产生"NaN"时,如何强制C#编译器引发异常?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!