system.math のソース コードをざっと見たところ、次のプログラムからわかるように、64 ビット バージョンの Delphi Tokyo 10.2.3 では非正規化 IEEE-Double がゼロにフラッシュされることが分かりました。
{$apptype console}
uses
system.sysutils, system.math;
var
x: double;
const
twopm1030 : UInt64 = $0000100000000000; {2^(-1030)}
begin
x := PDouble(@twopm1030)^;
writeln(x);
x := ldexp(1,-515);
writeln(x*x);
x := ldexp(1,-1030);
writeln(x);
end.
32ビットの場合、出力は予想通りです
8.69169475979376E-0311
8.69169475979376E-0311
8.69169475979376E-0311
しかし64ビットでは
8.69169475979375E-0311
0.00000000000000E+0000
0.00000000000000E+0000
したがって、基本的に Tokyo は 64 ビット モードで非正規数を処理でき、定数は正しく書き込まれますが、算術演算や ldexp を使用した場合でも、非正規数の結果はゼロにフラッシュされます。
この現象は他のシステムでも確認できますか? 確認できる場合、どこに文書化されていますか? (ゼロフラッシュに関して私が見つけることができた唯一の情報は、 ですDenormals become zero when stored in a Real48
)。
アップデート:私は知っている両方32 ビットと 64 ビットでは、単精度オーバーロードが使用されます。32 ビットでは x87 FPU が使用され、ASM コードはすべての精度 (単精度、倍精度、拡張) で実質的に同一です。FPU は常に 80 ビットの拡張を返し、これは早期切り捨てなしで倍精度に格納されます。64 ビットのコードは、格納する前に精度調整を行います。その間、私は問題レポートを提出しました (https://quality.embarcadero.com/browse/RSP-20925)、32 ビットまたは 64 ビットの一貫性のない結果に焦点を当てています。
ベストアンサー1
アップデート:
違いは、コンパイラがオーバーロードされた選択を処理する方法だけです。
@Graymatter が発見したように、LdExp
呼び出されたオーバーロードはSingle
32 ビット コンパイラと 64 ビット コンパイラの両方の型です。唯一の違いはコードベースで、32 ビット コンパイラは asm コードを使用しているのに対し、64 ビット コンパイラは purepascal 実装を使用しています。
正しいオーバーロードを使用するようにコードを修正するには、LdExp()
次のように最初の引数の型を明示的に定義します (64 ビット)。
program Project116;
{$APPTYPE CONSOLE}
uses
system.sysutils, system.math;
var
x: double;
const
twopm1030 : UInt64 = $0000100000000000; {2^(-1030)}
begin
x := PDouble(@twopm1030)^;
writeln(x);
x := ldexp(Double(1),-515);
writeln(x*x);
x := ldexp(Double(1),-1030);
writeln(x);
ReadLn;
end.
出力:
8.69169475979375E-0311
8.69169475979375E-0311
8.69169475979375E-0311
あなたのケースで選択されたオーバーロードされた関数は 型なので、
この動作は RTL バグとして報告されるべきだと思います
Single
Double
。結果の型は であり
、コンパイラはそれに応じて確実に適応するはずです。32
ビット コンパイラと 64 ビット コンパイラは同じ結果を生成するはずです。
注記浮動小数点型の型Double(1)
キャストは、Delphi 10.2 Tokyoで導入されました。以前のバージョンでの解決策については、double(10)のような型キャストを可能にするDelphiの最初のバージョンは何ですか?。