こんにちは,寺田です.
まず最初に,お恥ずかしながら,私はまだ XP ができていません.
CppUnit(私はC++プログラマ)は使っていますが,私が担当している箇所に関し
て,個人的かつ実験的に使用している状態です.今後,実際に使ってみた経験を
元にチーム内,社内に広めていきたいと考えている段階です.
さて,今回は浮動小数点演算/幾何演算のテストについて質問に参りました.
私は (3次元)CAD 屋です.従って,普段書いているプログラムはウェブサービ
ス等ではなく,浮動小数点演算が頻出する幾何計算プログラムです.この浮動小
数点演算というヤツは整数演算と比べるとなかなかの曲者のように思います.浮
動小数点演算をテストする場合には,整数演算とは違うコツがいるのではないか
と思い,質問に来ました.
以下,浮動小数点/幾何演算のテストで悩む例です.
例1)ベクトルクラスのテスト
単純なベクトルクラスを作りました.C++ の教科書にありがちな,+, - 等の演
算子がオーバーロードされているクラスです.また,内積や外積等の関数も揃え
ました.
テストは,最初は例えばこんな感じで作りました.
外積 outer_product() のテスト:
Vector a = ... ; // 乱数を使ってランダムなベクトルを生成
Vector b = ... ; // 乱数を使ってランダムなベクトルを生成
Vector c = outer_product( a, b ) ; // 外積を計算
CPPUNIT_ASSERT( inner_product( a, c ) == 0.0 ) ; // ダメ!
CPPUNIT_ASSERT( inner_product( b, c ) == 0.0 ) ; // ダメ!
このテストは,outer_product() が正しく書かれていても,殆ど失敗します.何
故なら,浮動小数点演算は誤差を含むため,a と c の内積がピッタリ 0 になる
ことは少ないからです.
従って,アサートを次のように書き換えることになります.
CPPUNIT_ASSERT( fabs(inner_product( a, c )) < 1.0e-4 ) ;
CPPUNIT_ASSERT( fabs(inner_product( b, c )) < 1.0e-4 ) ;
この手のテストの成否は,外積演算の入力 a, b の値に依存します.私の作った
テストは a, b を乱数で生成していますので,テストの実行のたびに,このテス
トは成功したり失敗したりします.
なんとも後味の悪いテストですが,どうにかならないものでしょうか.
例2)ニュートン法のテスト
関数の根を求める方法としてニュートン法は有名です.しかし,ニュートン法は
関数形によっては発散してしまいます.つまり,ニュートン法を使う際には,そ
の事前条件として「入力の関数形が発散しないもの」が成立する必要があります.
しかし,この事前条件の上手な記述方法は(多分)ありません.「とりあえずやっ
てみて発散したら条件違反」という判断方法しか無さそうです.
# Best-Effort 型のアルゴリズム?
こういう数値計算の場合,テストはどう書けばよいのでしょうか?
例3)複雑な幾何演算のテスト
ご存知のとおり,幾何形状に関する判断には,人間には見ただけですぐ判断でき
るのにコンピュータに判断させるのは非常に困難な場合が多く存在します.従っ
て,幾何演算の結果が正しいかどうかをテストコードで表すのは非常に難しい場
合が多いのです(場合によっては演算自体よりもテストコードの方が難しくなっ
てしまう).
それよりは,ビューに演算結果の形状を表示して人間の目で確かめた方がよっぽ
ど楽です.
この手のプログラムって,テストファーストは不可能だと思うのですが,如何で
しょうか.
正直なところ,この辺をごちゃごちゃ考えているとテストが面倒に思えてきて,
ついついテストなしでコーディングしてしまいます.CppUnit によるテストより
は,実コード中に assert() を埋め込んでいく方がよっぽど効率的で便利な方法
に感じることもしばしばです.
皆さんはどう思われますか?
なにかアドバイスがあれば是非お願いします.
以上です.
--
Y.Terada <terada@....jp>