11

您能否帮助我了解我在 Win32 平台上的 Delphi 应用程序中的 FPU Control Word 发生了什么。

当我们创建一个新的 VCL 应用程序时,控制字设置为 1372h。这是我不明白的第一件事,为什么它是 1372h 而不是单位中Default8087CW定义的 1332h System

这两者的区别:

1001101110010  //1372h
1001100110010  //1332h

是根据文档保留或未使用的第 6 位。

第二个问题是关于CreateOleObject.

function CreateOleObject(const ClassName: string): IDispatch;
var
  ClassID: TCLSID;
begin
  try
    ClassID := ProgIDToClassID(ClassName);
{$IFDEF CPUX86}
    try
      Set8087CW( Default8087CW or $08);
{$ENDIF CPUX86}
      OleCheck(CoCreateInstance(ClassID, nil, CLSCTX_INPROC_SERVER or
        CLSCTX_LOCAL_SERVER, IDispatch, Result));
{$IFDEF CPUX86}
    finally
      Reset8087CW;
    end;
{$ENDIF CPUX86}
  except
    on E: EOleSysError do
      raise EOleSysError.Create(Format('%s, ProgID: "%s"',[E.Message, ClassName]),E.ErrorCode,0) { Do not localize }
  end;    
end;

上述函数将控制字更改为137Ah,因此它打开了第 3 位(溢出掩码)。我不明白为什么它在Reset8087CW之后调用,而不是恢复进入函数之前的单词状态?

4

2 回答 2

6

第 6 位保留并忽略。这两个控制字实际上是相等的,因为 FPU 的行为相同。系统恰好设置了保留位。即使您尝试将值设置为$1332,系统也会将其设置为$1372。无论您要求第 6 位具有什么值,它都会被设置。因此,在比较这些值时,您必须忽略这一点。这里没什么好担心的。

至于CreateOleObject作者决定,如果您要使用该功能,那么您还将在使用 COM 对象时屏蔽溢出,甚至超越。谁知道他们为什么这样做,并且只针对 32 位代码?可能他们发现了一堆经常溢出的 COM 对象,因此添加了这个粘贴膏药。在创建时屏蔽溢出是不够的,在使用对象时也需要这样做,因此 RTL 设计人员选择从今以后取消屏蔽溢出。

或者也许这是一个错误。他们决定不修复 32 位代码,因为人们依赖于这种行为,但他们确实修复了 64 位代码。

无论如何,这个函数没有什么特别之处。你不需要使用它。您可以编写自己的代码来做您想做的事情。

使用互操作时,浮点控制是一个问题。Delphi 代码需要未屏蔽的异常。使用其他工具构建的代码通常会掩盖它们。理想情况下,当您调用 Delphi 代码并在返回时取消屏蔽它们时,您会屏蔽异常。期望其他库随意更改控制字。另请注意,这Set8087CW不是线程安全的,这是 Embarcadero 多年来拒绝解决的一个大问题。

没有简单的前进道路。如果您没有在程序中使用浮点,那么您可以简单地屏蔽异常并且可能没问题。否则,您需要确保在所有线程中的所有点都正确设置了控制字。一般来说,使用标准的 Delphi RTL 几乎是不可能的。我个人通过用线程安全版本替换 RTL 的关键部分来处理这个问题。我已在此 QC 报告中记录了如何执行此操作:QC#107411

于 2016-09-25T07:59:45.390 回答
1

免责声明:我在 Delphi XE 中调试了问题。

首先,第二个问题。

如果您查看代码,Set8087CW您会看到它将新的 FPU CW 值存储在Default8087CW变量中,并从;Reset8087CW恢复 FPU CW Default8087CW所以Reset8087CW调用 afterSet8087CW什么都不做,这证明了

Memo1.Lines.Clear;
Memo1.Lines.Add(IntToHex(Get8087CW, 4));   // 1372
Set8087CW( Default8087CW or $08);
Memo1.Lines.Add(IntToHex(Get8087CW, 4));   // 137A
Reset8087CW;
Memo1.Lines.Add(IntToHex(Get8087CW, 4));   // 137A

显然是一个错误。

现在是第一个问题 - 这是一个有趣的调试练习。

Delphi VCL 应用程序的值通过函数Default8087CW从十六进制 1332 更改为 1372 ,调用 from ,调用 from ,从单元的初始化部分调用。Windows.CreateWindowExClasses.AllocateHWndTApplication.CreateControls.pas

看看CreateWindowEx代码 - 它解释了会发生什么。我真的不想进一步讨论它 - Delphi 中的 FPU 支持太混乱和错误。

于 2016-09-25T09:22:48.677 回答