元派遣プログラマの自称技術系ブログです。雑記とか自作のオープンソースプロジェクトの話とか。
Javaとか組込とかできます。お仕事ください。

MSの罠

Managed Direct3DにNyARToolkitCSを組み込んだら、やたらと計算エラーが出る。
なんだろーな?と思って調べたら、どうやらdoubleで計算させてる部分が全部floatの計算結果になってしまっていたようだ。

計算結果に差がでる部分を突き詰めていくと、どうやらDirect3Dデバイスを作成する前とした後で、下記コードのe2とe1の値がずれる(倍精度が無効になる)事がわかった。

double e1, e2;
double[] p = new double[2];
p[0] = 0.77495574951171875;
p[1] = -0.017107900232076645;
e1 = 0.77495574951171875 * -0.017107900232076645;//v1[1] * v2[2];
e2 = p[0] * p[1];


どう考えても不具合だろう…、とおもって、"Direct3d Managed 浮動小数点"で検索をかけると…、

デバイス作成後の浮動小数点精度の維持

Device オブジェクトを作成すると、共通言語ランタイムは浮動小数点単位 (FPU) を単精度に変更し、バッファのパフォーマンスを維持します。共通言語ランタイムのデフォルトである、デフォルトの倍精度 FPU を維持するには、次のサンプル コードに示すように、Device オブジェクトを作成するときに CreateFlags.FpuPreserve フラグを使います。

Device device = null; // レンダリング デバイスを作成します
PresentParameters presentParams = new PresentParameters();

device = new Device(0, DeviceType.Hardware, this,
CreateFlags.SoftwareVertexProcessing |
CreateFlags.FpuPreserve, presentParams);

だそうで。
…、デフォルトで特殊なモードがONになってるあたり、さすがMS。


でもまあ、ARToolKitはそのまま単精度に置き換えると、エラーレートがとんでもなく上がる事がわかったのは収穫だった。