当任何数学运算在 .net 4 中产生“NaN”时,如何强制 C# 编译器抛出异常?
Posted
技术标签:
【中文标题】当任何数学运算在 .net 4 中产生“NaN”时,如何强制 C# 编译器抛出异常?【英文标题】:How do I force the C# compiler to throw an exception when any math operation produces 'NaN' in .net 4? 【发布时间】: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)
.
【参考方案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.***Exception' 的未处理异常”。 @Soraya 然后有一个*** :-) 看调用栈(Debug->Windows->Call Stack) 不幸的是调用堆栈不能帮助我。 “[外部代码]”是我的调用堆栈的内容:D @Soraya 只有“外部代码”?不能向下或向上滚动? 是的。无非是“外部代码”。用您的新代码替换 dll 并找到 nan 异常的确切位置后,代码是否在您的计算机上正常运行?以上是关于当任何数学运算在 .net 4 中产生“NaN”时,如何强制 C# 编译器抛出异常?的主要内容,如果未能解决你的问题,请参考以下文章