gpt4 book ai didi

floating-point - C++/CLI : SIGFPE, _control87,_fpreset,将古老的不受管理的Watcom C应用程序移植到.NET

转载 作者:行者123 更新时间:2023-12-04 04:27:37 26 4
gpt4 key购买 nike

我有一个数千行的应用程序,它依赖SIGFPE(由传递给signal()的函数指针处理)来更改状态,并在某些浮点条件发生时使代码正确运行。但是,在托管模式下的C++/CLI下,_control87生成在用C编写的静态库中执行的System.ArithmeticException。不支持_fpreset和_control87。

如何使经典的,不受管理的SIGFPE操作在C++/CLI应用程序中工作?在我的应用程序中发生浮点运算的位置可能很多,而且我还不完全理解其他程序员几年前编写的所有数值方法。

我希望老式的异常处理能够将浮点数除以零,而不是INF值。平台调用样式不起作用,并且#pragmamanaged(off)也不起作用。

我有什么选择?

最佳答案

这里有几个非常严重的痛点。启用浮点异常与托管代码执行完全不兼容。简而言之,您可以轻松使JIT编译器崩溃。使用_control87()时,您要解决的问题是哪一个。

是的,您将获得CLR异常,每当它执行 native 代码时,它将放置一个异常支持。仅在引发异常并且没有代码可处理时,才调用信号处理程序。 CLR不可避免地会在C运行时库看到该异常之前就看到该异常。因此,您将永远不会收到SIGFPE处理程序调用。

唯一可行的解​​决方法是编写一个包装,以便在CLR之前捕获异常。仔细管理FPU控制字也非常非常重要,您只能承受在运行 native 代码时启用FPU异常的能力。这需要一堆坚韧的代码,预先警告您将不会非常喜欢它。

您没有发布任何代码段,因此我必须组成一个愚蠢的示例:

#include <Windows.h>
#include <signal.h>
#include <float.h>

#pragma managed(push, off)

double divisor;

void __cdecl fpehandler(int sig) {
divisor = 1.0;
}

double badmath() {
divisor = 0.0;
return 1 / divisor;
}
#pragma managed(pop)

为了调用fpehandler(),您需要在C运行时库中调用异常处理程序。幸运的是,它是公开的,您可以链接它,只需为其声明即可进行调用:
// Exception filter in the CRT, it raises the signal
extern "C" int __cdecl _XcptFilter(unsigned long xcptnum,
PEXCEPTION_POINTERS pxcptinfoptrs);

您需要确保仅调用浮点异常。因此,我们需要一个包装器来关注异常代码:
int FloatingpointExceptionFilter(unsigned long xcptnum, PEXCEPTION_POINTERS pxcptinfoptrs) {
// Only pass floating point exceptions to the CRT
switch (xcptnum) {
case STATUS_FLOAT_DIVIDE_BY_ZERO:
case STATUS_FLOAT_INVALID_OPERATION:
case STATUS_FLOAT_OVERFLOW:
case STATUS_FLOAT_UNDERFLOW:
case STATUS_FLOAT_DENORMAL_OPERAND:
case STATUS_FLOAT_INEXACT_RESULT:
case STATUS_FLOAT_STACK_CHECK:
case STATUS_FLOAT_MULTIPLE_TRAPS:
case STATUS_FLOAT_MULTIPLE_FAULTS:
return _XcptFilter(xcptnum, pxcptinfoptrs);
break;
default:
return EXCEPTION_CONTINUE_SEARCH;
}
}

现在,您可以为badmath()编写一个包装器,该包装器将调用称为:
double badmathWrapper() {
__try {
return badmath();
}
__except (FloatingpointExceptionFilter(GetExceptionCode(), GetExceptionInformation())) {
}
}

C++/CLI类又可以调用该类,您可以从任何托管代码中调用该类。它需要确保在调用之前启用浮点异常,并在调用之后再次恢复浮点异常:
using namespace System;
using namespace System::Runtime::CompilerServices;

public ref class Wrapper {
public:
static double example();
};

[MethodImplAttribute(MethodImplOptions::NoInlining)]
double Wrapper::example() {
signal(SIGFPE, fpehandler);
_clear87();
unsigned oldcw = _control87(_EM_INEXACT, _MCW_EM);
try {
return badmathWrapper();
}
finally {
_control87(oldcw, _MCW_EM);
signal(SIGFPE, nullptr);
}
}

注意对_control87()的调用,它启用了除“inexact result”外的所有 float 异常。这是允许代码被伪装的必要条件。如果您不掩盖它,那么CLR会死于可怕的死亡,一遍又一遍地抛出异常,直到该站点的名称终结了它。希望您的信号处理程序不需要它。

关于floating-point - C++/CLI : SIGFPE, _control87,_fpreset,将古老的不受管理的Watcom C应用程序移植到.NET,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3338534/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com