C#,VS 2010
我需要确定浮点值是否为NaN。
使用
测试NaN的浮点数float.IsNaN(aFloatNumber)
堆栈溢出崩溃。
也是如此
aFloatNumber.CompareTo(float.NaN).
以下内容不会崩溃,但它无用,因为无论如何都会返回NaN:
aFloatNumber - float.NaN
搜索"堆栈溢出"返回有关此网站的结果,而不是实际堆栈溢出的结果,因此我无法找到相关答案。
为什么我的应用程序在测试NaN时会进入堆栈溢出?
编辑:调用堆栈:
编辑:我的代码中显然有些东西:这句话:
bool aaa = float.IsNaN(float.NaN);
所以,这就是我在做的事情:
编辑:
Debug.WriteLine()显示代码只执行一次:no recursion。
编辑:
这有效:
float fff = 0F;
int iii = fff.CompareTo(float.PositiveInfinity);
崩溃了:
float fff = 0F;
int iii = fff.CompareTo(float.NaN);
答案 0 :(得分:25)
在自定义控件的类的构造函数中正常工作
这是对潜在问题的唯一真实暗示。在线程上运行的代码可以操作处理器内的两个堆栈。一个是每个人都知道的正常的,并给这个网站起了名字。然而,还有另一个,隐藏在FPU(浮点单元)内部。它在进行浮点计算时存储中间操作数值。这是8级深。
FPU中的任何类型的不幸事件都是而不是应该生成运行时异常。 CLR假设FPU配置了FPU控制字的默认值,它可以生成的硬件异常应被禁用。
当您的程序使用来自20世纪90年代的代码时,确实有一个出错的诀窍,当时启用FPU异常仍然听起来像个好主意。例如,Borland工具生成的代码因此而臭名昭着。其C运行时模块重新编程 FPU控制字并取消屏蔽硬件异常。您可以获得的异常类型可能非常神秘,在代码中使用NaN是触发此类异常的好方法。
这应该至少部分可见于调试器。在“仍然良好”的代码上设置断点并使用Debug + Windows + Registers调试器窗口。右键单击它并选择“浮点”。您将看到所有与浮点计算有关的寄存器,例如ST0到ST7是堆栈寄存器。这里重要的一个标记为CTRL
,它在.NET进程中的正常值是027F
。该值的最后6位是异常屏蔽位(0x3F),全部打开以防止硬件异常。
单步执行代码,期望您看到CTRL值更改。一旦它发生,你就会找到邪恶的代码。如果启用非托管调试,那么您还应该在“输出”窗口中看到加载通知,并看到它出现在“调试+ Windows +模块”窗口中。
撤消DLL所造成的损害是相当尴尬的。你必须在msvcrt.dll中修改_control87(),例如恢复CTRL字。或者您可以使用的简单技巧,您可以故意抛出异常。 CLR内部的异常处理逻辑重置FPU控制字。幸运的是,这种代码可以解决您的问题:
InitializeComponent();
try { throw new Exception("Please ignore, resetting FPU"); }
catch {}
您可能需要移动它,接下来最好的猜测是Load事件。调试器应该告诉你在哪里。
答案 1 :(得分:6)
我刚刚写了一个例子来重现错误: 1.创建导出此函数的本机C / C ++ DLL:
extern "C" __declspec(dllexport) int SetfloatingControlWord(void)
{
//unmask all the floating excetpions
int err = _controlfp_s(NULL, 0, _MCW_EM);
return err;
}
2。创建一个C#控制台程序,调用函数SetfloatingControlWord,然后执行一些浮动操作,如NaN比较,然后导致堆栈溢出。
[DllImport("floatcontrol.dll")]
public static extern Int32 SetfloatingControlWord();
static void Main(string[] args)
{
int n = SetfloatingControlWord();
float fff = 0F;
int iii = fff.CompareTo(float.NaN);
}
我在几年前遇到了同样的问题,同样,我注意到在.NET异常抛出后,一切正常,我花了一段时间找出原因并跟踪改变FPU的代码。
正如函数文档_controlfp_s所说:默认情况下,运行时库会屏蔽所有浮点异常。公共语言运行库(CLR)仅支持默认的浮点精度,因此CLR不处理这些类型的异常。
正如MSDN所述:默认情况下,系统已关闭所有FP异常。因此,计算导致NAN或INFINITY,而不是异常。
NaN在IEEE 754 1985中引入后,假设应用软件不再需要处理浮点异常。
答案 2 :(得分:2)
解决方案:
首先,感谢@Matt指出我正确的方向,感谢@Hans Passant提供解决方法。
该应用程序与中国制造商QM_CAN的CAN-USB适配器进行通信。
问题出在他们的司机身上。
DLL语句和驱动程序导入:
// DLL Statement
IntPtr QM_DLL;
TYPE_Init_can Init_can;
TYPE_Quit_can Quit_can;
TYPE_Can_send Can_send;
TYPE_Can_receive Can_receive;
delegate int TYPE_Init_can(byte com_NUM, byte Model, int CanBaudRate, byte SET_ID_TYPE, byte FILTER_MODE, byte[] RXF, byte[] RXM);
delegate int TYPE_Quit_can();
delegate int TYPE_Can_send(byte[] IDbuff, byte[] Databuff, byte FreamType, byte Bytes);
delegate int TYPE_Can_receive(byte[] IDbuff, byte[] Databuff, byte[] FreamType, byte[] Bytes);
// Driver
[DllImport("kernel32.dll")]
static extern IntPtr LoadLibrary(string lpFileName);
[DllImport("kernel32.dll")]
static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);
对违规代码的调用,包括Hans的解决方法:
private void InitCanUsbDLL() // Initiate the driver for the CAN-USB dongle
{
// Here is an example of dynamically loaded DLL functions
QM_DLL = LoadLibrary("QM_USB.dll");
if (QM_DLL != IntPtr.Zero)
{
IntPtr P_Init_can = GetProcAddress(QM_DLL, "Init_can");
IntPtr P_Quit_can = GetProcAddress(QM_DLL, "Quit_can");
IntPtr P_Can_send = GetProcAddress(QM_DLL, "Can_send");
IntPtr P_Can_receive = GetProcAddress(QM_DLL, "Can_receive");
// The next line results in a FPU stack overflow if float.NaN is called by a handler
Init_can = (TYPE_Init_can)Marshal.GetDelegateForFunctionPointer(P_Init_can, typeof(TYPE_Init_can));
// Reset the FPU, otherwise we get a stack overflow when we work with float.NaN within a event handler
// Thanks to Matt for pointing me in the right direction and to Hans Passant for this workaround:
// http://stackoverflow.com/questions/25205112/testing-for-a-float-nan-results-in-a-stack-overflow/25206025
try { throw new Exception("Please ignore, resetting FPU"); }
catch { }
Quit_can = (TYPE_Quit_can)Marshal.GetDelegateForFunctionPointer(P_Quit_can, typeof(TYPE_Quit_can));
Can_send = (TYPE_Can_send)Marshal.GetDelegateForFunctionPointer(P_Can_send, typeof(TYPE_Can_send));
Can_receive = (TYPE_Can_receive)Marshal.GetDelegateForFunctionPointer(P_Can_receive, typeof(TYPE_Can_receive));
}
}
当在事件处理程序中而不是在构造函数中引用float.NaN时,应用程序崩溃的原因很简单:在InitCanUsbDLL()之前调用构造函数,但事件处理程序被调用为long在InitCanUsbDLL()损坏了FPU寄存器之后。