スポンサーサイト

--年--月--日 --:--

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

浮動小数点エラーの取得

2006年04月26日 16:41

ちょっとプログラムで悩んでることがあるのでそっちがまとまったらまた記事書きます。





C++でカリー化を実装するために悩んでるんですけどね、、、
単純なカリー化や Lambda はできました。
しかし!式をデータとして埋め込み、遅延評価できるところが上手くまとまりません・・・。
戻り値の型を限定すれば出来るんですけどね、、、柔軟に書くのはやっぱり無理なのかなー('A`)





閑話休題。
今回は「コレ、使えるんだけど暫く経ったら忘れそうだなぁ」っていうテクニックを書きます。
ってことで、タイトルどおり浮動小数点エラーの取得方法です。

浮動小数点のエラーを発見する方法の一つに signal 関数を使う方法があります。





void Handler( int code )
{
  /* なんか処理する */
}


// どこか、プログラムの本文で signal 関数を呼んでやる
{
  ....

  signal( SIGFPE, Handler );
}





ってな感じに書けば例外処理をかけます。
signal 関数は他にも無効な命令や無効なメモリー領域を読み込むときなどの例外も割り込みで処理できます。

しかし、ここで問題が・・・。
signal での処理では 0除算などは、割り込みが聞かないのです( ;Д⊂ヽ
前は効いた気がするんだけど、、、なんだろ?
nan になるタイプの計算以外は例外を発生させないのかな?


ちなみに、強制的に例外を発生させる命令に raise 関数があります。
自分で書いた例外処理をデバックしたい場合によく使います。



そんでもってここからが本番。
Infinity になった浮動小数点や Denormal になった浮動小数点を知りたい!ってことがあると思います。
なんでかっていうと、、、そんな Overflow や Underflow になった浮動小数点で計算していると、計算結果もまた異常な値になるからです。

んで、それをチェックする関数を書きます。

int _fpclass( double ) 関数です。

見ればわかるとおりに、double を引数として持ちます。
引数の double がどんな値なのか?を調べる関数ですね。


んで、戻ってくるステータスコードは、、、

  _FPCLASS_SNAN  0x0001 符号付の NAN になってる。
  _FPCLASS_QNAN  0x0002 NAN になってる。
  _FPCLASS_NINF  0x0004 - の無限大
  _FPCLASS_NN   0x0008 - の通常値(表現できる値)
  _FPCLASS_ND   0x0010 - の Denormal な値
  _FPCLASS_NZ   0x0020 - の 0
  _FPCLASS_PZ   0x0040 + の 0
  _FPCLASS_PD   0x0080 + の Denormal な値
  _FPCLASS_PN   0x0100 + の通常値(表現できる値)
  _FPCLASS_PINF  0x0200 + の無限大

ってなります。
普段使う時は、

~(_FPCLASS_NN|_FPCLASS_NZ|_FPCLASS_PZ|_FPCLASS_PN)

でマスク(ビット論理和)してやれば、異常値だけとることが出来ます。


今回書く理由になったバグは、コンバーターが0除算してる場所があって、
例外飛ばさないで計算続けてたのが原因でした・・・ orz

Infinity になってる浮動小数点で計算すると Denormal な値しか返さなくなるので、
表示できる数字に戻すことが出来ません。

それでデータが壊れてたんですけど、画面に表示されるまでわかりませんでした('A`)


blog_rank


スポンサーサイト

Cgシェーダープログラム で Direct X のような頂点宣言を実現する方法

2006年04月10日 11:43

今日はすこぶる眠いです。。

っていうのも、昨日午前半3時半ぐらいまでFF12やってたからなんですけどねw
やっと最後のダンジョンの手前まで来ました。
物語の展開自体は、安っぽい感じなので先が読めますがケリをつけると意味で今日中にクリアできたらいいなーって思ってます。



んで、需要がありそうなプログラムの話。

例によって今日説明する関数名をGoogle等で引っかかりやすいようにあげておきます。
「cgGetFirstParameter」「cgGetParameterVariability」「cgGetParameterSemantic」「CG_VARYING」「CG_UNIFORM」「CG_PROGRAM」「CG_GLOBAL」



前回解説した中で省いた「Cg シェーダープログラムで Direct X の頂点宣言のような処理をやるには」を書きます。
要するに、シェーダーとアプリケーションのプログラムを独立させたいってことですね。

これが出来ないとアプリケーションは常にシェーダーの中身を必ず知っていないとダメです。変数名まで。
もちろん、決められた変数名を使用すればいいんですけど、そんなルールで動くプログラムなんてスペルミス一つでバグが発生しそうだし、第一カッコ悪すぎます。

そんなこんなで、やりたいことは、、、




頂点要素(ここでは位置や法線、テクスチャのUV値、スキンウェイト、接線、準法線などのことをさします)の
パラメータ(データ配置位置・タイプ・使用方法・インデックスなど)を渡すことでシェーダーに渡されるストリームを
自動的に解釈して、関連付ける。




が、出来ればいいわけですね。


さて、やりたいことが決まったらそれを実現するための方法を考えましょう。
まず、シェーダープログラムの内容を解釈しないとダメみたいです。

 問題1:どうやって読み込んだシェーダープログラムを解釈するか?
 問題2:プログラムを解釈した時、入力パラメーターを探せるのか?
 問題3:もし入力パラメーターを特定したとしても、
     そのパラメーターが頂点のどのデータに対応しているのかがわかるのか?

この3つがわかれば、同じようなことが出来そうです。


長々書くのも嫌なので、それぞれの回答を書きます。

 問題1の回答:
   cgGLLoadProgram で読み込んだプログラムのハンドル(CGprogram)を使って
   cgGetFirstParameter 関数を使います。
   このとき引数に、CG_PROGRAM と CG_GLOBAL のどちらかを使えますが、
   念のため二つとも使用して(プログラムの中のパラメーターを)調べた方がいいでしょう。
   それで、cgGetFirstParameter では CGprogram の中の最初のパラメーター(CGparameter)が
   帰ってきます。
   このパラメーターを cgGetNextParameter 関数に渡すことで、
   次のパラメーターを取得することが出来ます。
   後は NULL が帰ってくるまで再帰的に cgGetNextParameter を呼べば
   すべての CGparameter を取得することが出来ます。


 問題2の回答:
   cgGetParameterVariability 関数を使います。
   列挙した CGparameter 変数を渡し、
   戻り値が
    CG_VARYING なら入力パラメーター
    CG_UNIFORM なら定数レジスタ
   になります。


 問題3の回答:
   cgGetParameterSemantic 関数を使います。
   この関数は与えられたパラメーターのセマンティックを文字列で返します。
   セマンティックとは頂点シェーダーなどで
   
    struct OUTPUT
    {
      float4 position : POSITION;
      float4 color   : COLOR0;
      float4 T0    : TEXCOORD0;
      float4 T1    : TEXCOORD1;
      float4 T2    : TEXCOORD2;
      float4 T3    : TEXCOORD3;
    };
   
   などで、変数名の後ろについてる POSITION などの文字をさします。
   ちなみに、CG では Varying タイプで使用できるセマンティックは
   各プロファイルごとに決まっています。
   
   後は文字列を strcmp などで処理して自分なりの頂点宣言を作れば終わりです。



こうやって書くと物凄いオーソドックスな方法で当たり前のことをやってます。
しかし、こういうことを説明している日本語のHPはこれまた皆無なので、ここに記述しておきます。

サンプルプログラムは暇を見つけて書いたら次回アップします。



実は今日解説した方法を使用することで、シェーダープログラムの定数レジスタにも Direct X の HLSL と同じようにインデックス番号でアクセスできるようにする方法があります。
こっちはもうちょっと複雑なので時間があったら書きます。


blog_rank




最近の記事


上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。