【GLSL TIPS】円周率の定義とfloat精度


GLSLで円周率の定義方法と用いるfloatの精度についての覚書きです。

別に知らなくても問題ないので大抵は気にしなくていいやつです。

円周率πの定義

GLSLでは円周率が定義されていないので自分で用意する必要があります。

#define PI 3.14なんかもわかりやすくていいんですが、ちゃんと定義したいなら逆三角関数を使うと簡単に定義できます。

 

  • acos(-1.)
  • asin(1.)*2.
  • atan(1.)*4.

cos(π)=-1, sin(π/2)=1, tan(π/4)=1を逆三角関数でπを算出してます。

 

以下を差し込むとわかりますが、どれを使っても同じです

シンプルで文字数も少ないacosがいいと思うんですがatanを使ってるのをよく見かけます。まぁ好きな奴で。

一緒にτも定義しておくと便利です。

 

【短く書きたいときの参考】18文字で書けるπ精度

  • [改行]#define P 3.1415[改行]
  • float p=3.1415926;
  • float p=acos(-1.);

float 浮動小数点の比較について

適当に数値を使った処理をしていると時折気になる動作をしていたので、ちゃんと考えてみる。

GLSLの精度は?

まずprecision highp floatの場合、精度はどうなるのか。

OpenGLの定義を見てみるとこんな感じです。

https://www.khronos.org/files/opengles_shading_language.pdf

最低限これを満たすってことだろうけどわかりにくい。

 

WebGL2.0の元になるES 3.0ではこのように書かれていました。

The precision of highp floating-point variables is defined by the IEEE 754 standard for 32-bit floatingpoint numbers. (参照元)

わかりやすい。通常の単精度浮動小数点数ですね。

 

ただ定義ではこうなっていても環境(実装)次第で変わるので、以下のテストコードなんかも参考程度にしかならないと思います。

同値比較の失敗

一般に浮動小数点数同士の同値比較はNGです。

 

試しに円周率ベースで実験。

大分違和感のある結果になります。

計算もなく数値比較するだけでこれくらいの誤差が出てきます。

 

他言語だとfalse positive(OKなのにNG)な判定が多い気がするけど、GLSLではfalse negative(NGなのにOK)な判定になる気がします。

つまり普通に比較しちゃってもある程度は想定通り動きます

本当はどうするのがいいか

float同士の比較をするなら差分が0に近いかを見る必要があります。

この比較する数値は一般的にε(Epsilon)として、これより小さな値は誤差ということにしています。

単精度なので10進数だと大体6, 7桁くらいの精度のはずです。ε=1e-5くらいでいいんじゃないでしょうか。

相対で精度2-16が保証されていますがそっちで計算するのも違う気がする。

そもそも小数点何桁まで処理できるの?

どこまで小さな数を使えるんだろうという疑問がわいた。

試しに以下のように書いていって、どこまで比較可能か確かめました。

1e-37まで使えるようですね。

 

2の累乗で境界を探してみよう。

単精度浮動小数点数(IEEE 754)の限界値まで処理出来る(使い道は相当限られるだろうけど)。

 

πの話に戻ると以下のGLSLの結果は赤。つまりもう差が0ですね。

この式でepsilonに2の累乗を使うと 2-126 まで赤で 2-127以降は黒でした。


コメントを残す

メールアドレスが公開されることはありません。