これは何の話?
三角関数 sin や cos の角度を表すとき、数学では一般的に**ラジアン(radian)**を使う。
1周なら、
2π rad
90°なら、
π/2 rad
となる。
しかし、プログラミングでは、
「1周 = 1」として、そのまま何周したかで角度を表したほうが分かりやすく、計算にも都合がいいのでは?
という考え方がある。
この表し方が**turns(周回数)**である。
たとえば、
0.25 turns = 90°
0.5 turns = 180°
1.0 turn = 360°
となる。
今回の発表では、Casey Muratoriによる2022年の記事 Turns are Better than Radians を出発点として、
本当にturnsのほうが効率的なのか?
本当にturnsのほうが数値的に正確なのか?
を考え、さらに実際に500万ステップの数値実験を行って検証した。
その結果、
turnsは便利だが、turnsに変えるだけで計算精度が良くなるわけではない。
という、元記事の主張から一歩進んだ結論にたどり着いた。
まず結論
今回の発表の結論を一言でまとめると、
turnsは魔法じゃない。厳密さは、折返し次第。
である。
turnsには、
「何周したか」がそのまま読める
1/4周、1/2周などを2進浮動小数点で厳密に表現できる
位相を0〜1で扱うプログラムと相性がいい
といった利点がある。
しかし、
turnsを使えば常にラジアンより数値精度が高くなる
わけではない。
実際に位相を何百万回も加算する実験では、素朴なturns実装のほうがラジアン実装より大きな誤差を示した。
一方、
64bit整数で1周を表現し、整数の桁あふれによって周期を折り返す方式
では、今回の条件では圧倒的に小さな誤差になった。
つまり重要なのは単位そのものより、
周期をどのような数値表現で持ち、どう折り返すか
だった。
Knowledge ― そもそもラジアンとは?
ラジアンは、円の弧の長さから角度を定義する。
半径を r、弧の長さを s とすると、
θ = s / r
となる。
特に半径1の単位円なら、
角度の数値 = 円周上を進んだ距離
になる。
1周の円周は 2π なので、
1周 = 2π rad
となる。
なぜ数学ではラジアンが強い?
ラジアン最大の利点は、微積分と非常にきれいにつながることにある。
ラジアンなら、
d/dx sin(x) = cos(x)
となる。
しかしturnsを u とすると、
sin(2πu)
になるため、微分すると、
2π cos(2πu)
という係数が付く。
つまり、
ラジアンは単に昔から使われている単位なのではなく、微積分をすると式が自然になるような角度の測り方
でもある。
物理学で角速度を rad/s として扱うことや、オイラーの公式とのつながりも、この性質と関係している。
turnsとは?
turnsでは、
円を1周したら1
と定義する。
対応関係は単純で、
| 角度 | ラジアン | turns |
|---|---|---|
| 0° | 0 | 0 |
| 90° | π/2 | 0.25 |
| 180° | π | 0.5 |
| 270° | 3π/2 | 0.75 |
| 360° | 2π | 1 |
となる。
つまり、
同じ円周上の位置に、違う目盛りを振っている
だけである。
turnsの直感的な強さ
たとえば、
3.5 turns
と言われれば、
3周半
とすぐ分かる。
同じものをラジアンで表すと、
7π rad
になる。
数学的には自然でも、
「何周した?」
という意味を直接読み取るならturnsのほうが分かりやすい。
特に、
オーディオの位相
アニメーション
シェーダ
周期処理
など、もともと値を [0, 1) の範囲で扱う用途とは相性がよい。
Practice ― なぜ「ラジアンへの変換は無駄」と言われたのか?
元記事で例として挙げられたのが、次のようなコードである。
もともと、
h = 0.1
という「0〜1の周期位置」を持っている。
それを、
h × 2π
としてラジアンへ変換し、sin()へ渡す。
すると、
0.1 × 2π ≈ 0.628
になる。
しかし記事で例示された高速sin実装では、範囲縮約のため入力へ、
4 / π
を掛ける。
すると、
0.1 × 2π × 4/π
となり、
0.1 × 8 = 0.8
になる。
つまり、
せっかく2πを掛けたのに、三角関数内部ではπを使って再び別の周期表現へ変換している。
それなら、
最初から周期として扱えば、この往復はいらないのでは?
というのが元記事の中心的な主張だった。
ただし、すべての sin() 実装が必ずこの処理を行うという意味ではなく、元記事で取り上げられた高速実装を例にした議論である。
turnsなら角度を正確に保存できる?
元記事ではturnsの利点として、
0.25
0.5
0.75
などの値を浮動小数点数で正確に表せることも挙げられていた。
ラジアンなら90°は、
π/2
であり、πは無理数なので有限ビットでは完全には表現できない。
一方、
0.25 = 1/4
なら2進数でも有限桁で表現できる。
これは確かにturnsの利点である。
しかし、ここで今回の発表では疑問が出た。
じゃあ60°は?
Inherent Talent ― turnsでも正確にならない角度がある
60°は、
1/6 turn
である。
120°なら、
1/3 turn
になる。
しかし2進数で有限桁として正確に表せる分数は、
約分した分母が2の累乗になっているもの
に限られる。
たとえば、
1/2
1/4
1/8
は表現できる。
しかし、
1/3
1/6
1/12
などは、2進数でも無限に続く。
つまり、
45° = 1/8 turn → 厳密
90° = 1/4 turn → 厳密
だが、
30° = 1/12 turn → 非厳密
60° = 1/6 turn → 非厳密
120° = 1/3 turn → 非厳密
となる。
したがって、
「turnsなら角度を正確に表現できる」
と一般化することはできない。
より正確には、
「2進表現と相性のよい一部の角度では、turnsが非常にきれいに表現できる」
という話である。
「正確に保存できる」と「長時間計算しても正確」は別
ここからが今回の発表独自の検証になる。
ある角度を一回保存するだけなら、turnsは確かに便利な場合がある。
しかし実際のプログラムでは、
少しずつ角度を足し続ける
ことがある。
たとえばオシレータなら、
現在の位相 + Δ
を何百万回も繰り返す。
このとき、
小さな丸め誤差がどのように蓄積するのか?
を調べた。
3つの方式で500万回回してみる
実験では、
Δ = 0.0137293 turns / step
を500万回加算した。
そして3方式を比較した。
A:ラジアン方式
位相をラジアンで保持する。
2π
を超えたら、
2π
を引いて折り返す。
B:turns方式
位相をturnsで保持する。
1.0
を超えたら、
1.0
を引いて折り返す。
最後に 2π を掛けて sin を計算する。
C:64bit整数方式
1周を64bit整数全体に割り当てる。
位相を整数として加算する。
桁あふれすると、自動的に次の1周へ折り返す。
これはNCOやBAMなどで使われる固定小数点的な位相表現である。
基準は60桁の高精度計算
どれが正確なのかを調べるため、通常のfloat64ではなく、60桁の任意精度演算による値を基準にした。
その基準値と各方式の sin 出力との差を測定した。
結果
500万ステップ後の絶対誤差は、おおよそ、
ラジアン方式
2.061 × 10^-10
turns方式
8.820 × 10^-10
64bit整数方式
2.133 × 10^-16
となった。
つまり今回の条件では、
64bit整数方式が、浮動小数点による2方式より6桁以上小さな誤差だった。
ところがturnsのほうが悪かった
さらに意外だったのが、
素朴なturns方式のほうが、ラジアン方式より約4〜5倍大きな誤差を示した
ことだった。
「turnsのほうが数字を正確に扱えるのだから、誤差も少ないだろう」
という直感とは逆の結果になった。
ここで重要なのは、
一部の角度を厳密に表せることと、何百万回も加算したときに誤差が小さいことは別問題
ということである。
なぜturnsの誤差が大きくなった?
発表・補足資料では、
完全には説明し切れていない
という状態になっている。
一つの仮説としては、turnsの位相誤差は最終的に、
sin(2πu)
へ変換される。
この関数を u で微分すると、
2π cos(2πu)
となるため、位相方向の小さな誤差が出力へ反映される際には 2π のスケールが関係する。
しかし、これだけでは今回観測された、
約4.2〜4.3倍
という安定した比率を完全には説明できていない。
増分 0.0137293 の具体的な浮動小数点表現なども影響している可能性がある。
そのため、
「turnsはラジアンより4.28倍悪い」
という一般則が得られたわけではない。
今後、増分を変えて再実験する必要がある。
Value ― 本当に効いていたのは「単位」ではなかった
今回最も大きな結果は、
ラジアンかturnsかより、折返しをどう実装するかのほうがはるかに大きな影響を持っていた
ことである。
浮動小数点方式では、
足す
↓
境界を超える
↓
2πまたは1を引く
という操作を何度も繰り返す。
そのたびに有限精度の浮動小数点演算が入る。
一方64bit整数では、
1周そのものを整数空間の一周として設計する。
最大値を超えると、
mod 2^64
として自然に先頭へ戻る。
この折返し自体には浮動小数点丸めが入らない。
今回使った増分については64bit表現にも十分な精度があったため、500万ステップ後でも非常に小さな誤差に留まった。
つまり、
周期的な量を扱うなら、「値そのものを周期構造に合った表現へする」という設計が強い。
ということが見えてきた。
turnsは意味がないのか?
そういう話でもない。
turnsには、
何周したか読みやすい
0〜1のphase表現と自然につながる
1/2、1/4、1/8周などが2進数と非常に相性がいい
という明確なメリットがある。
一方ラジアンには、
微積分
物理
複素解析
既存の数学ライブラリとの互換性
という非常に強いメリットがある。
したがって、
どちらかを世界統一単位にするというより、用途に応じて選ぶ
という考え方が現実的になる。
実はhalf-turn関数はすでに存在する
この考え方は完全な新発明でもない。
いくつかの数値計算環境では、
sin(πx)
を直接計算するための、
sinpi
系の関数が存在する。
これは入力 x をhalf-turnとして考えることができる。
たとえば、
sin(π × 1)
を普通にfloatで計算すると、π自体が近似値なので完全な0にならないことがある。
一方、専用の sinpi(1) なら、
「整数なら数学的に必ず0」
という構造を利用して厳密な結果を返せる。
つまり実際の数値計算ライブラリでも、
何でもラジアンの実数へ変換してから計算するのではなく、周期構造を保ったまま三角関数へ渡す
という設計には意味がある。
質疑応答から見えてきたこと
ラジアンとturnsのいいとこ取りはできない?
議論では、
「ラジアンの微積分のきれいさと、turnsの扱いやすさを両方持てないのか?」
という話が出た。
しかしこれは単純に一つの単位へ統合するというより、
どこで何を表現するか
を分ける問題に近い。
数式・微分方程式ではラジアン。
周期の保存やphase accumulatorではturnsや固定小数点。
必要な場所で変換する。
つまり、
単位の勝敗ではなく、レイヤーごとの使い分け
として考えるほうがよい。
「誤差が10^-10」って大きいの?
発表のグラフでは誤差が対数スケールで表示されていたため、
どれくらい違うのか
についても議論された。
今回の誤差自体は非常に小さい。
しかし重要なのは絶対値だけではなく、
同じ処理を繰り返したときに、どの方式で誤差が増えていくのか
を見ることにある。
長時間動作するオシレータや非常に多数のステップを扱う場合、こうした性質が重要になる。
1/3 turn問題は意外と重要
turnsについて、
1/4なら厳密
という説明は分かりやすい。
しかし、
1/3
1/6
では同じ利点が失われる。
この点は元記事の象限角の例だけを見ると見落としやすい。
つまり、
分かりやすい成功例から、単位全体の性質を一般化しない
ことも今回の検証から得られたポイントだった。
さらに広げると「単位って何?」になる
補足資料では、さらに根本的な話として、
そもそも角度に「単位」はあるのか?
という議論まで広がっている。
たとえば、
45
という数字だけ渡されても、
45°
なのか、
45 rad
なのか、
45 turns
なのか分からない。
人間が暗黙に、
「三角関数の入力ならラジアンだよね」
と約束しているだけとも言える。
角度を無次元量として扱う現在の慣習についても、計量学では議論が続いている。
つまり今回の、
「sinの引数は本当にラジアンである必要がある?」
というプログラミング上の疑問は、
角度とは何を数値化したものなのか
という、かなり根本的な問題にもつながっていく。
RPMにも同じ「周回」の発想がある
日常的にも、回転そのものを数える単位は普通に使われている。
たとえば、
RPM = Revolutions Per Minute
は、
1分間に何回転するか
を表している。
つまり回転機械では最初から、
ラジアンではなく「何周したか」
を基準に表現することが自然な場面もある。
turnsという発想そのものが奇妙なのではなく、
目的によって「自然な角度の物差し」が違う
と考えたほうが分かりやすい。
この発表から考えられること
今回の発表は一見すると、
ラジアン vs turns、どっちが強い?
という単位論争に見える。
しかし実際に検証してみると、もっと一般的な教訓が見えてくる。
元記事:
「turnsのほうが正確!」
↓
実際に考える:
「本当に全部の角度?」
↓
1/3 turn:
「正確じゃないです。」
↓
さらに実験する:
「長時間累積したら?」
↓
turns:
「ラジアンより誤差大きくなりました。」
↓
固定小数点:
「単位より実装じゃない?」
となった。
つまり、
もっともらしい主張を、そのまま受け入れず「どこまで本当なのか」を実際に条件を変えて試す
ことで、最初とは違う問題が見えてきた。
今回の場合、
「どの角度単位が正しい?」
だった問いが、最後には、
「周期的な量をコンピュータ上でどう表現すれば壊れにくい?」
という問いへ変わった。
この問いの変化そのものが、今回の発表のおもしろいところでもある。
今後調べたいこと
今回の検証では、まだいくつか未解決の点が残っている。
- turns方式がラジアン方式より約4.2〜4.3倍大きな誤差になった理由を理論的に説明する
Δを複数の値へ変えて同じ傾向が再現するか確認する- degree方式も含めて比較する
10^8〜10^12ステップ程度まで伸ばした場合の固定小数点方式を調べる- 実際のCPU/GPUの三角関数命令で比較する
sinpi系の専用実装が、通常のsin(πx)に対してどの程度有利なのか検証する
つまり今回の結論も、
これでturns論争が決着した
というものではない。
むしろ、
実際に計算してみたら次の問いが出てきた
という状態である。
まとめ
ラジアンは、
円弧の長さを基準にした、微積分と非常に相性のよい角度表現
である。
一方turnsは、
1周を1として扱う、周期や回転を直感的に表現できる角度表現
である。
turnsなら、
90° = 0.25
180° = 0.5
のような角度を2進浮動小数点で厳密に表せる。
しかし、
60° = 1/6
120° = 1/3
などは厳密にはならない。
さらに位相を500万回累積する実験では、
素朴なturns方式は、素朴なラジアン方式よりも誤差が大きかった。
一方、64bit整数による固定小数点方式では、今回の実験条件で圧倒的に小さな誤差となった。
つまり、
数値計算の精度を決めていた本質は「ラジアンかturnsか」だけではなく、「周期をどう表現し、どう折り返すか」だった。
turnsにはturnsの強みがある。
ラジアンにはラジアンの強みがある。
だから、
一つの単位を万能だと考えるのではなく、何を計算したいかに応じて表現を選ぶ。
というのが今回の持ち帰りになる。
👶 ドパガキ向け:つまり何?
プログラマー:
「1周を0〜1で管理してます!」
↓
sin:
「角度ラジアンでくれ。」
↓
プログラマー:
「じゃあ2π掛けます!」
↓
高速sin実装:
「計算しにくいからπで割って小さくします!」
↓
プログラマー:
「?????」
↓
turns派:
「最初から1周=1でよくね?????」
turns派:
「しかも90°は0.25だからコンピュータで完全に表せる!!!!」
↓
60°:
「1/6です。」
turns派:
「…………」
2進数:
「それ無限小数です。」
turns派:
「全部正確なわけではなかった。」
研究:
「じゃあ実際500万回回してみよ!」
↓
ラジアン:
ちょっとずれる
turns:
もっとずれる
研究:
「お前のほうがズレるんかい!!!!」
↓
64bit整数:
ほぼズレません。
研究:
「???????」
整数:
「1周したら桁あふれで勝手に0へ戻るので。」
研究:
「折返しが強い!!!!!!!!」
超結論
「turnsにしたら全部正確!」ではなかった。
turnsは、
分かりやすい!
周期処理と相性いい!
1/4周とかはめちゃきれい!
でも、
1/3周は普通に丸まる!
何百万回も足したら誤差も貯まる!
そして実験したら、
単位より「どう1周を折り返すか」のほうがめちゃくちゃ効いた。
つまり、
ラジアン vs turns!!!!!
と思って殴り合わせたら、
横から64bit整数が来て、
「お前ら単位じゃなくて実装の話しろよ」
と全部持っていった話。
turnsは魔法じゃない。
厳密さは、折返し次第。
参照文献・資料
Muratori, C. (2022, September 26). Turns are Better than Radians. Computer, Enhance!.
Mohr, P. J., Shirley, E., Phillips, W. D., & Trott, M. (2022). On the dimension of angles and their units. arXiv:2203.12392.
MathWorks. sinpi — Compute sin(Xpi) accurately*. MATLAB Documentation.
Microsoft. .NET Trigonometric Functions — SinPi. Microsoft Learn / .NET API Documentation.
※ 数値実験については今回のN.E.M.会発表用に作成された独自検証であり、「turns方式がラジアン方式より常に約4.3倍誤差が大きい」という一般則を示したものではない。
