6

浮動小数点決定論を必要とする C アプリケーションを開発しています。また、浮動小数点演算をかなり高速にしたいと考えています。これには、サインや対数など、IEEE754 で指定されていない標準の超越関数が含まれます。私が検討したソフトウェア浮動小数点の実装は、ハードウェア浮動小数点に比べて比較的遅いため、各回答から最下位ビットの 1 つまたは 2 つを単純に丸めることを検討しています。精度の低下はアプリケーションにとって十分な妥協ですが、これはプラットフォーム全体で決定論的な結果を保証するのに十分でしょうか? すべての浮動小数点値は double になります。

演算の順序が、浮動小数点の結果のばらつきのもう 1 つの潜在的な原因であることは認識しています。私はすでにそれに対処する方法を持っています。

今日使用されている主要な浮動小数点ハードウェア実装のソフトウェア実装があれば素晴らしいので、このような仮説を直接テストできます。

4

1 に答える 1

3

私が理解しているように、浮動小数点の加算や乗算などのIEEE標準演算で表現されたsin(x)のような超越関数のソフトウェア実装があり、すべてのマシンで同じ答えを得たいと思っています(または、少なくとも、関心のあるすべてのマシン)。

まず、理解してください。これはすべてのマシンに移植できるわけではありません。たとえば、IBM メインフレームの 16 進浮動小数点は IEEE ではないため、同じ答えは得られません。正確に取得するには、FP の加算や乗算などの IEEEE 標準演算のソフトウェア実装が必要です。

IEEE 標準の浮動小数点を実装するマシンだけに関心があると思います。また、NaN は IEEE 754-1985 によって完全に標準化されておらず、HP と MIPS の 2 つの反対の実装が発生したため、NaN について心配していないことも推測しています。1

これらの制限がある場合、どのようにして計算の変動性を得ることができますか?

(1) コードが並列化されている場合。そうなっていないことを確認してください。(可能性は低いですが、マシンによっては可能性があります。) 並列化は、FP の結果変動の主な原因です。私が知っている少なくとも 1 つの会社は、再現性と並列性を重視し、FP の使用を拒否し、整数のみを使用しています。

(2) マシンが適切にセットアップされていることを確認します。

たとえば、ほとんどのマシンは 32 ビットまたは 64 ビットの精度で計算します (C の元の標準はどこでも 64 ビットの "double" でした。しかし、Intel x86/x87 はレジスタで 80 ビットで計算でき、スピル時に 64 または 32 に丸められます。1は、インライン アセンブリを使用した 80 ビットから 64 ビットへの x86/x87 精度制御. このコードはアセンブリ レベルであり、移植性がないことに注意してください. x87 80ビット。

(ちなみに、x86 では、SSE FP を使用することによってのみすべての問題を回避できます。古いレガシー Intel x87 FP は、まったく同じ答えを与えることはできません (ただし、精度制御 (PC) を 80 ビットではなく 64 ビットに設定すると、指数幅は影響を受けず、仮数のみであるため、中間オーバーフローがあった場合を除いて、同じ結果が得られます))

たとえば、すべてのマシンで同じアンダーフロー モードを使用していることを確認します。つまり、非標準化または有効化を保証するか、逆にすべてのマシンがフラッシュ トゥ ゼロ モードであることを確認します。これは Dobson の選択です。flush to zero モードは標準化されていませんが、一部のマシン (GPU など) は単純に非正規化された数値を持っていません。つまり、多くのマシンには IEEE 標準の数の FORMAT がありますが、実際の IEEE 標準の算術演算 (デノルムを使用) はありません。私は、IEEE の非規範化を要求することを強く望んでいますが、もし私が完全に偏執的であるなら、ゼロへのフラッシュを使用し、ソフトウェアで自分自身をフラッシュすることを強制します。

(3) 同じ言語の ioptions を使用していることを確認してください。古い C プログラムはすべての計算を「double」(64 ビット) で行いますが、現在では単精度での計算が許可されています。いずれにせよ、すべてのマシンで同じ方法で実行したいでしょう。

(4) コードに関するいくつかの小さな項目:

コンパイラが再配置する可能性が高い大きな式を避ける (厳密な FP スイッチを適切に実装していない場合)

次のような単純な形式ですべてのコードを書くことができます

double a = ...;
double b = ...;
double c = a *b;
double d = ...;
double e = a*d;
double f = c + e;

それよりも

f = (a*b) + (a*c);

に最適化される可能性があります

f = a*(b+c);

コンパイラ オプションについては長くなるので、最後に説明します。

これらすべてを実行すると、計算は完全に再現可能になります。IEEE 浮動小数点は正確です。常に同じ答えが得られます。変動性を導入するのは、IEEE FP への途中でのコンパイラによる計算の再配置です。

下位ビットを四捨五入する必要はありません。しかし、そうしても害はなく、いくつかの問題を隠す可能性があります. 覚えておいてください: 追加ごとに少なくとも 1 ビットをマスクする必要があるかもしれません....

(2) 異なるマシンで異なる方法でコードを再配置するコンパイラーの最適化。あるコメンターが言ったように、コンパイラが厳密な FP に切り替えるものは何でも使用してください。

sin コードを含むファイルのすべての最適化を無効にする必要がある場合があります。

揮発性物質を使用する必要がある場合があります。

より具体的なコンパイラ スイッチがあることを願っています。たとえば、gcc の場合:

-ffp-contract=off --- すべてのターゲット マシンに融合乗加算があるとは限らないため、融合乗加算を無効にします。

-fexcess precision=standard --- 内部レジスタの Intel x86/x87 超過精度などを無効にします

-std=c99 --- かなり厳密な C 言語標準を指定します。残念ながら、今日グーグルで調べたところ、完全には実装されていません

-funsafe-math や -fassociativbe-math などの最適化が有効になっていないことを確認してください

于 2012-02-25T04:16:07.487 に答える