为什么应用程序以与Default8087CW不同的FPU控制字启动?

时间:2016-09-25 06:41:20

标签: delphi com fpu delphi-10.1-berlin

请您帮我理解在Win32平台上的Delphi应用程序中FPU Control Word的运行情况。

当我们创建一个新的VCL应用程序时,控制字设置为1372h。这是我不了解的第一件事,为什么它是1372h而不是1332h,这是Default8087CW单位中定义的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,而不是恢复进入函数之前的单词状态?

2 个答案:

答案 0 :(得分:5)

第6位保留并被忽略。在FPU表现相同的意义上,这两个控制字实际上是相等的。系统恰好设置了保留位。即使您尝试将值设置为$1332,系统也会将其设置为$1372。无论你问第6位有什么价值,它总会被设置。因此,在比较这些值时,您必须忽略该位。这里没什么值得担心的。

对于CreateOleObject,作者决定如果你打算使用那个函数,那么你在使用COM对象时也会掩盖溢出,实际上超出了。谁知道他们为什么这样做,而且只支持32位代码?可能他们发现了一堆常常溢出的COM物体,因此添加了这种贴膏药。在创建时屏蔽溢出是不够的,在使用对象时也需要这样做。所以RTL设计者选择从今后取消屏蔽溢出。

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

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

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

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

答案 1 :(得分:1)

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

首先,第二个问题。

如果您查看Set8087CW的代码,您会看到它将新的FPU CW值存储在Default8087CW变量中,Reset8087CWDefault8087CW恢复FPU CW;所以Reset8087CW之后的Set8087CW调用什么都不做,这可以通过

来证明
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值由Windows.CreateWindowEx函数从十六进制1332更改为1372,从Classes.AllocateHWnd调用,从TApplication.Create调用,从初始化部分调用Controls.pas单位。

查看CreateWindowEx代码 - 它解释了会发生什么。我真的不想进一步讨论它 - 德尔福的FPU支持过于混乱和错误。