Delphi 为什么应用程序以不同于Default8087CW的FPU控制字开始?

Delphi 为什么应用程序以不同于Default8087CW的FPU控制字开始?,delphi,com,fpu,delphi-10.1-berlin,Delphi,Com,Fpu,Delphi 10.1 Berlin,您能帮助我了解一下在Win32平台上我的Delphi应用程序中FPU控制字的使用情况吗 当我们创建一个新的VCL应用程序时,控制字设置为1372h。这是我不明白的第一件事,为什么它是1372h而不是1332h,1332h是System单元中定义的Default8087CW 这两者之间的区别: 1001101110010 //1372h 1001100110010 //1332h 是根据文件保留或未使用的第6位 第二个问题涉及CreateOleObject function CreateOl

您能帮助我了解一下在Win32平台上我的Delphi应用程序中FPU控制字的使用情况吗

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

这两者之间的区别:

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
,而不是恢复进入函数之前的单词状态

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

至于
CreateOleObject
,作者决定,如果您要使用该函数,那么在使用COM对象时,您还将屏蔽溢出,甚至更高。谁知道他们为什么这么做,而且只针对32位代码?可能他们发现了一堆经常溢出的COM对象,所以添加了这种膏药。仅仅在创建时屏蔽溢出是不够的,在使用对象时也需要这样做,因此RTL设计者选择从现在开始取消屏蔽溢出

或者可能是一只虫子。他们决定不对32位代码进行修复,因为人们依赖这种行为,但他们确实对64位代码进行了修复

在任何情况下,这个函数都没有什么特别的作用。你不需要使用它。你可以写你自己的,做你想做的事

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


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

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

第一,第二个问题

如果查看
Set8087CW
的代码,您将看到它将新的FPU CW值存储在
Default8087CW
变量中,并且
Reset8087CW
Default8087CW
恢复FPU CW;因此,
Set8087CW
之后的
Reset8087CW
调用根本不做任何事情,如

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
函数从
Classes调用,从
TApplication.Create
调用,从
Controls.pas
单元的初始化部分调用,从十六进制1332更改为1372


看看
CreateWindowEx
code-它解释了发生了什么。我真的不想进一步讨论它-Delphi中的FPU支持太混乱和有缺陷。

在您的QC报告中下载了该文件。请注意,文件名被截断,没有扩展名。添加
.zip
修复了该问题。遗憾的是,@AllenBauer在船上时无法修复那个长期存在的RTL错误。@LURD我想他是想修复的,但管理层不允许。谢谢,这就是我的想法。我没有想到将CW设置为$1332 tho是不可能的。请注意,Delphi RTL在各种地方都使用浮点,包括内存管理(例如,
Move()
)的一些变体),因此很难保证您在某个地方没有使用浮点。@Marc在该示例中,这不是一个问题,因为所做的只是使用寄存器而不是执行算术。
Reset8087CW
确实做了一些事情。它将控制字设置为当前可在
Default8087CW
中找到的任何内容。如果像经常发生的那样,
CoCreateInstance
修改了控制字,那么这很可能会产生效果。你的其他分析是错误的
CreateWindowEx
确实调用
Set8087CW
,但它将调用返回的值传递给
Get8087CW
。祝您好运,让FP装置保持住$1332
。它只是不允许您这样做。因此,如果您更仔细地调试它,您将看到FPU是在调用
\u FpuInit
时初始化的,该调用来自
系统的初始化部分。它传递
Default8087CW
中的任何内容,通常是
$1332
,但系统强制设置第6位,因此设置的值是
$1372
。对
CreateWindowEx
@DavidHeffernan的任何调用都与此无关-分析是正确的,我已经跟踪了调试器中
Default8087CW
的更改。FPU CW从不设置为
$1332
,并且
CreateWindowEx
Default8087CW
设置为