11

Win32 プラットフォームの Delphi アプリケーションで FPU コントロール ワードで何が起こっているのかを理解するのを手伝ってくれませんか。

新しい VCL アプリケーションを作成すると、コントロール ワードは 1372h に設定されます。これは私が理解できない最初のことです。なぜユニットでDefault8087CW定義されている1332hではなく1372hなのか。System

これら2つの違い:

1001101110010  //1372h
1001100110010  //1332h

ドキュメントによると、予約されているか使用されていない6番目のビットです。

2 番目の質問は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 番目のビットは予約されており、無視されます。これら 2 つの制御ワードは、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 で質問をデバッグしました。

まず、2つ目の質問です。

のコードをSet8087CW見ると、新しい FPU CW 値がDefault8087CW変数に格納され、 Reset8087CWFPU CW が から復元されていることがわかりDefault8087CWます。したがって、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によって、16 進数の 1332 から 1372 に変更されます。Windows.CreateWindowExClasses.AllocateHWndTApplication.CreateControls.pas

CreateWindowExコードを見てください- 何が起こるかを説明しています。これ以上議論するつもりはありません。Delphi での FPU サポートは面倒でバグが多すぎます。

于 2016-09-25T09:22:48.677 に答える