私は.NETの浮動小数点決定論について多くのことを読んでいます。つまり、同じ入力を持つ同じコードが異なるマシン間で同じ結果をもたらすことを保証します。.NETにはJavaのfpstrictやMSVCのfp:strictなどのオプションがないため、純粋なマネージコードを使用してこの問題を回避する方法はないというのがコンセンサスのようです。C#ゲームのAI Warsは、代わりに固定小数点演算を使用することに決めましたが、これは面倒な解決策です。
主な問題は、CLRにより、中間結果がタイプのネイティブ精度よりも高い精度のFPUレジスタに存在できるようになり、予想外に高い精度の結果が得られることです。CLRエンジニアのDavidNotarioによるMSDNの記事では、次のように説明しています。
現在の仕様では、「予測可能性」を与えることは依然として言語の選択であることに注意してください。言語は、すべてのFP操作の後にconv.r4またはconv.r8命令を挿入して、「予測可能な」動作を取得する場合があります。 明らかに、これは本当に高価であり、言語が異なれば妥協点も異なります。たとえば、C#は何もしません。絞り込みが必要な場合は、(フロート)キャストと(ダブル)キャストを手動で挿入する必要があります。
これは、浮動小数点と評価されるすべての式と部分式に明示的なキャストを挿入するだけで、浮動小数点の決定論を実現できることを示しています。このタスクを自動化するために、floatの周りにラッパータイプを書くかもしれません。これはシンプルで理想的なソリューションです!
しかし、他のコメントは、それがそれほど単純ではないことを示唆しています。Eric Lippertは最近述べました(私の強調):
ランタイムの一部のバージョンでは、floatにキャストすると、明示的にキャストしない場合とは異なる結果が得られます。明示的にfloatにキャストすると、C#コンパイラはランタイムに「この最適化を使用している場合は、これを超高精度モードから外してください」というヒントを提供します。
ランタイムへのこの「ヒント」は何ですか?C#仕様では、floatへの明示的なキャストにより、ILにconv.r4が挿入されると規定されていますか?CLR仕様では、conv.r4命令によって値がネイティブサイズに絞り込まれることが規定されていますか?これらの両方が当てはまる場合にのみ、David Notarioによって説明されているように、浮動小数点の「予測可能性」を提供するために明示的なキャストに依存できます。
最後に、実際にすべての中間結果をタイプのネイティブサイズに強制できる場合でも、これはマシン間の再現性を保証するのに十分ですか、それともFPU / SSEランタイム設定などの他の要因がありますか?