4

決定論的であり、異なるプラットフォーム (コンパイラ) で同じ結果を提供する必要があるプログラムの場合、組み込みの三角関数は使用できません。これは、それを計算するアルゴリズムがシステムによって異なるためです。結果値が異なることがテストされました。

(編集: すべてのクライアントで実行されるゲーム シミュレーションで使用されるため、結果は最後のビットまでまったく同じである必要があります。これらのクライアントは、シミュレーションを機能させるために、シミュレーションの状態がまったく同じである必要があります。エラーは、時間の経過とともにますます大きなエラーになる可能性があり、ゲーム状態の crc も同期のチェックとして使用されます)。

したがって、私が思いついた唯一の解決策は、独自のカスタム コードを使用してこれらの値を計算することでした。問題は、(驚くべきことに) 三角関数のすべてのセットの使いやすいソース コードを見つけるのが非常に難しいことです。

これは、sin 関数用に取得したコード ( https://codereview.stackexchange.com/questions/5211/sine-function-in-cc ) を変更したものです。これはすべてのプラットフォームで決定論的であり、値は標準の sin の値とほぼ同じです (両方ともテスト済み)。

#define M_1_2_PI 0.159154943091895335769 // 1 / (2 * pi)

double Math::sin(double x)
{
  // Normalize the x to be in [-pi, pi]
  x += M_PI;
  x *= M_1_2_PI;
  double notUsed;
  x = modf(modf(x, &notUsed) + 1, &notUsed);
  x *= M_PI * 2;
  x -= M_PI;

  // the algorithm works for [-pi/2, pi/2], so we change the values of x, to fit in the interval,
  // while having the same value of sin(x)
  if (x < -M_PI_2)
    x = -M_PI - x;
  else if (x > M_PI_2)
    x = M_PI - x;
  // useful to pre-calculate
  double x2 = x*x;
  double x4 = x2*x2;

  // Calculate the terms
  // As long as abs(x) < sqrt(6), which is 2.45, all terms will be positive.
  // Values outside this range should be reduced to [-pi/2, pi/2] anyway for accuracy.
  // Some care has to be given to the factorials.
  // They can be pre-calculated by the compiler,
  // but the value for the higher ones will exceed the storage capacity of int.
  // so force the compiler to use unsigned long longs (if available) or doubles.
  double t1 = x * (1.0 - x2 / (2*3));
  double x5 = x * x4;
  double t2 = x5 * (1.0 - x2 / (6*7)) / (1.0* 2*3*4*5);
  double x9 = x5 * x4;
  double t3 = x9 * (1.0 - x2 / (10*11)) / (1.0* 2*3*4*5*6*7*8*9);
  double x13 = x9 * x4;
  double t4 = x13 * (1.0 - x2 / (14*15)) / (1.0* 2*3*4*5*6*7*8*9*10*11*12*13);
  // add some more if your accuracy requires them.
  // But remember that x is smaller than 2, and the factorial grows very fast
  // so I doubt that 2^17 / 17! will add anything.
  // Even t4 might already be too small to matter when compared with t1.

  // Sum backwards
  double result = t4;
  result += t3;
  result += t2;
  result += t1;

  return result;
}

しかし、asin、atan、tan (sin/cos 以外) など、他の関数に適したものは見つかりませんでした。

これらの関数は、標準のものほど正確ではありませんが、少なくとも 8 つの数字があればよいでしょう。

4

4 に答える 4

3

最も簡単なのは、必要な数学関数を実装するリベラルなランタイム ライブラリを選択することだと思います。

  • FreeBSD
  • go、音訳が必要になりますが、すべての関数には非アセンブリ実装があると思います
  • MinGW-w64
  • ...

そして、それらの実装を使用するだけです。上記のリストは、パブリック ドメインまたは BSD ライセンス、またはその他のリベラル ライセンスのいずれかであることに注意してください。コードを使用する場合は、必ずライセンスを遵守してください。

于 2014-05-27T07:45:20.193 に答える
2

テイラー級数が使えます(実は知らず知らずのうちに使っているものらしい)

ウィキペディア (またはその他の場所) をご覧ください: https://en.wikipedia.org/wiki/Taylor_series

最も一般的な関数 (exp、log、cos、sin など...) のリストがここにあります 。https://en.wikipedia.org/wiki/Taylor_series#List_of_Maclaurin_series_of_some_common_functions(明らかにすべてではありませんが...)

いくつかの例(他にもたくさんあります)

テイラー1

ノート:

  1. 追加する用語が多いほど、精度が高くなります。
  2. 必要なものを計算する最も効率的な方法だとは思いませんが、非常に「単純な」方法です(私が意味するアイデア)
  3. あなたがそれを使うことに決めたfactorial(n)なら、関数は本当に役に立つかもしれません

それが役立つことを願っています。

于 2014-05-26T19:24:52.730 に答える
0

ルックアップ テーブルと線形/バイキュービック補間の使用を検討することをお勧めします。

そうすれば、各ポイントで値を正確に制御でき、非常に多くの乗算を実行する必要がなくなります。

とにかく、sin/cos関数のテイラー展開はひどい

spring rts は、この種の desync エラーに対して何年も戦いました: フォーラムに投稿してみてください。

このスレッドhttp://springrts.com/phpbb/viewtopic.php?f=1&t=8265では、特に libm の決定論について話しています (ただし、os によって libc が異なり、微妙な最適化の違いがある可能性があるため、アプローチしてスローする必要があります図書館)

于 2014-05-28T11:28:48.887 に答える