HN 日本語サマリー

← 一覧へ戻る
プログラミング

ターンはラジアンより優れている

Turns are Better than Radians (computerenhance.com)

302 pointsby mayoff158 コメント

要約

この記事は、コンピュータグラフィックスや数学で一般的に使用される角度の単位であるラジアンから「ターン」(円周全体を1とする単位)への移行を推奨しています。ラジアンは円周率π(パイ)またはタウ(2π)を必要とし、多くの計算で不要な乗算や丸め誤差を生じさせますが、ターンを使用するとπやタウを排除でき、コードがシンプル、高速、かつ高精度になります。多くのライブラリが内部でラジアンから他の形式に変換している現状を踏まえ、ターンへの切り替えは実用的で有益であると主張しています。

全文翻訳

ターンはラジアンより優れている ラジアンからターンに切り替えることで、コードがよりシンプルに、高速に、そして高精度になります。 Casey Muratori 2022年9月26日 12947 シェア しばらく前、円周率(3.14159…)の近似値の代わりにタウ(6.28318…)の近似値を使用するように人々を説得するために多くの努力が費やされました。多くのブログ記事やYouTube動画によると、その考えは、一般的な公式がよりシンプルになり、半円ではなく円全体を表す定数の方が扱いやすいというものでした。 概ね同意します。些細な点ですが、指摘する価値はあります。ほとんどのコードは、πをタウに置き換えるとわずかに改善されます。しかし、その熱狂の最中に、より影響力の大きい機会が見過ごされていました。πをタウに置き換える代わりに、ほとんどの場合、πは完全に削除できます。 それがどのように機能するかを見てみましょう。 まず、コードにおけるπとタウの一般的なケースを考えます。それは、三角関数の呼び出しのためにラジアンに変換したり、ラジアンから変換したりすることです。これらの定数を使用したことがあるなら、書いたコードの大部分は次のようなものだったでしょう。 y = center.y + (center.y * Math::sin(h * Math_TAU) * s) - (cursor->get_height() / 2); これは私が例を作成したのではなく、GitHubのGodot Engineのソースコードをランダムに開き、「tau」を検索したものです。上記のコード片や、それに類する多数の使用例が、それを見つけることができます。 ここにはGodotに特別なものはありません。ランダムなゲームエンジンのコードベースを開けば、全く同じ検索を行い、全く同じ種類の使用例を見ることができます。 何が起こっているかに注目してください。プログラマーは、0から1の範囲で既に周期的な値hを持っていますが、sinを呼び出す必要があるため、タウを掛けます。 もしそれ以上深く見なければ、これは非常に理にかなっているように思えるかもしれません。しかし、sinの実装はどうでしょうか? sinの実装は数多くありますが、どの実装を見ても、関数のエントリポイントの近くに次のようなものが見つかるでしょう。 _PS256_CONST(cephes_FOPI, 1.27323954473516); ... y = _mm256_mul_ps(x, *(v8sf*)_ps256_cephes_FOPI); これも私が例をでっち上げたのではなく、この一般的に参照されるAVX2実装のsinからのものです。これは珍しいことでも奇妙なことでもありません。ほとんどすべての高速三角関数ライブラリは、非常に似たことを行うでしょう。 この行は何をしていますか?入力に定数1.27323954473516を掛けます。これはちょうど4/πに等しい値です。 したがって、呼び出し元のコードは次のようにしています。 sin(h * 2 * pi) しかし、ライブラリコードはすぐに次のようにします。 y = (4 / pi) * x これは、呼び出し元のコードがπという係数を掛けているだけで、ライブラリコードがそれをすぐに割り戻していることを意味します。両方のプログラマーが単にラジアンを使用しないことに同意し、代わりにhが既に持っていた元の[0, 1]ドメインを使用したとすれば、両方の作業が単純になります。呼び出し元は乗算を節約でき、ライブラリは理解しやすい正確な定数を得られます。 そして、「正確」という点は実際には非常に興味深いです。ラジアンに不必要に変換すると追加の乗算コストがかかるだけでなく、0以外のすべての一般的なラジアン角度の表現が困難であることも注目に値します。90度をラジアンで保存したいですか?ビット数をどれだけ使っても、正確になることはありません。 一方、[0, 1]での90度は単に0.25であり、マントッサのビットを全く必要としないビットパターンです!0.5は?同じです!0.75は?正確に表現するために必要なマントッサのビットは1つだけです。 したがって、[0, 1]の範囲は計算効率が高いだけでなく、実用的な使用で頻繁に発生する典型的な値を表現する際に、よりコンパクトで高精度です。 数学はラジアンを必要としません。 なぜ一部の人々がこの切り替えを心配するのか理解できます。たとえすべてのユーザーコードがπまたはタウを掛けており、すべてのライブラリコードがそれを割り戻していると私が言っても、ラジアンの使用をやめると間違ったことをしているような「数学の授業の感覚」を抱くかもしれません。 しかし、数学はサインとコサインがラジアン引数を取らなければならないと命じたわけではありません! 円をゼロからタウではなくゼロから1でパラメータ化するという考えは、私がこのブログ記事のために作ったランダムなアイデアではありません。それは実際に正当で既存の数学的構造であり、名前さえあります。それはターンと呼ばれます。 ターンでは、0は0度、0.5は180度、1は360度、2は720度などです。まさに私たちが望んでいたものです。 したがって、数学の先生があなたに怒るのではないかと心配しているなら、心配する必要はありません。あなたが慎重に検討し、問題の最も効率的な方法としてラジアンではなくターンで角度をパラメータ化することを決定したと彼らに伝えればよいのです! 切り替えは簡単です。 独自の数学ライブラリを作成したか、誰かのものをプロジェクトにコピーした場合、ラジアンから離れてコードベースからπとタウを削除する方法は非常に明確でしょう。あなたがしなければならないのは、サインとコサインの関数を取り、それらをラジアンではなくターンを受け取るようにすることだけです。通常、これは単一の定数の簡単な調整にすぎません。 レガシーコードをサポートしたい場合は、新しいターンベースの三角関数に別の名前を付けます。次に、レガシーコードのために、古いラジアンベースのサインとコサインを、タウによる除算を途中で行いながら、新しいルーチンを介して呼び出すようにすることで、引き続きサポートできます。 切り替えを行うためのコードはほんの数行で、非常にシンプルです。 しかし、ターンが最も便利な再パラメータ化であると私が考えている一方で、それが唯一の代替手段ではありません。特に、独自の数学ルーチンを作成しない場合(そして作成する場合でさえ)、代わりにハーフターンを使用することを検討するかもしれません。この場合、完全な円は[0, 2]になります。少し混乱しますが… それは既にいくつかのライブラリに存在します! 実際に(意図した通り!)探してみると、一部の数学ライブラリには、ラジアンではなくハーフターンでパラメータ化されたサインとコサイン関数が既に存在します。例えば、CUDAのsincospi組み込み関数は、入力にπを掛けたサインとコサインを計算します。これはハーフターンです。 これは素晴らしいことです。sincospiが既に利用可能なプラットフォームをターゲットにしている場合、ライブラリに一切触れることなく、コードでπとタウ定数を使用するのを今すぐやめることができます。単にラジアンベースのsinとcosの代わりにハーフターンでsincospiを呼び始めれば、準備完了です。 タウとπが少ないほど良い。 コードベース全体でラジアンの使用をやめた経験から、それらを恋しく思ったことは一度もありません。すべての余分なタウとパイが消え、すべてがより明確に読めるようになります。サインとコサインを変更するための同じロジックは、他の標準的な三角関数にも適用されるため、選択すればラジアンをどこでも削除できます。ライブラリは内部でほとんどの場合ラジアンから変換し、その後ラジアンに戻すため、すべてをターンまたはハーフターンに切り替えることは、通常、コードを削除するだけで、それ以上のことはあまりありません。 Computer, Enhance! をお読みいただきありがとうございます。新しい投稿を受け取るには無料で購読してください。 購読 12947 シェア 前へ 次へ