请您帮我理解在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
,而不是恢复进入函数之前的单词状态?
答案 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
变量中,Reset8087CW
从Default8087CW
恢复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支持过于混乱和错误。