1. ホーム
  2. c#

[解決済み] C#で浮動小数点演算は整合性が取れているのか?可能なのか?

2022-04-25 20:52:32

質問

いいえ、これは別のものではありません。 "なぜ (1/3.0)*3 != 1" なのか。 という質問をします。

最近、浮動小数点数についてよく読んでいるんだ。 同じ計算でも結果が異なる場合がある アーキテクチャや最適化の設定が異なる場合

これは、リプレイを保存するビデオゲームで問題となる、あるいは ピアツーピアネットワーク型 (サーバー・クライアント型ではなく) プログラムを実行するたびに、すべてのクライアントがまったく同じ結果を生成することに依存している場合、1 つの浮動小数点演算の小さな不一致が、マシンによってゲームの状態が大きく異なることにつながります (あるいは 同じマシンで )

これは、quot;follow"しているプロセッサの間でも起こります。 IEEE-754 これは主に、一部のプロセッサ(すなわち x86)が 倍拡張精度 . つまり、80ビットのレジスタを使ってすべての計算を行った後、64ビットまたは32ビットに切り捨てるため、64ビットまたは32ビットの計算を行うマシンと異なる丸め誤差が発生するのです。

この問題に対する解決策をいくつかネットで見ましたが、すべてC#ではなくC++のものでした。

  • 倍精度拡張モードを無効にする(これにより、すべての double を使用して、IEEE-754 の 64 ビットを使用して計算します。 _controlfp_s (Windows)の場合。 _FPU_SETCW (Linux?)、または fpsetprec (BSD)です。
  • 常に同じコンパイラを同じ最適化設定で実行し、すべてのユーザーが同じCPUアーキテクチャを持っていることを要求する(クロスプラットフォームプレイはしない)。 なぜなら、私の "コンパイラ" は実際には JIT であり、これは プログラムを実行するたびに異なる最適化が行われる可能性があります。 これは無理だと思います。
  • 固定小数点演算を使用し floatdouble を完全に削除します。 decimal はこの目的のために機能しますが、はるかに遅くなり、すべての System.Math ライブラリ関数がサポートしています。

では。 は、C#でも問題ないのでしょうか? Windowsだけをサポートするつもりなら(Monoではなく)どうすればいいのでしょうか?

もし、そうなら。 は、私のプログラムが通常の倍精度で実行されるように強制する方法はあるのでしょうか?

そうでない場合は 何か便利なライブラリはありますか? 浮動小数点演算の一貫性を保つことができますか?

どのように解決するのですか?

.netで通常の浮動小数点を決定論的にする方法を私は知りません。JITterは、異なるプラットフォーム(または.netの異なるバージョン)間で異なる動作をするコードを作成することが許可されています。そのため、通常の float を決定論的な.netのコードで使用することは不可能です。

私が考えた回避策

  1. C#でFixedPoint32を実装する。これはそれほど難しくないが(私は半分ほど実装を終えた)、値の範囲が非常に小さいので、使うのが煩わしい。オーバーフローしないように、また、精度が落ちないように、常に気をつけなければならない。最終的には、整数を直接使うより簡単ではないことがわかった。
  2. C#でFixedPoint64を実装する。これはかなり難しいと思った。128bitの中間整数があれば便利なこともある。しかし、.net にはそのような型はない。
  3. カスタム32ビット浮動小数点を実装する。BitScanReverse 組込み関数がないため、これを実装する際にいくつかの問題が発生します。しかし、現在のところ、これが最も有望な道だと思います。
  4. 数学演算にネイティブコードを使用する。すべての数学演算でデリゲート呼び出しのオーバーヘッドが発生します。

私は、32ビット浮動小数点演算のソフトウェア実装を始めたばかりです。私の2.66GHzのi3で毎秒約7000万回の足し算/掛け算が可能です。 https://github.com/CodesInChaos/SoftFloat . もちろん、まだ非常に未完成でバグも多いのですが。