4

だから私はユニットテストを行うためにCUnitを使用しています。私は次のようなものを期待しています

float x;
x = atof("17.99");

そして、アサートでこれをテストしたいと思います。明らかに、私ができるいくつかの小さなイプシロンで

CU_ASSERT(abs(x - atof("17.99")) < epsilon);

検討中ですが

r = atof("17.99");
CU_ASSERT(0 == memcmp(&x, &r, sizeof(float));

これはうまくいくようです。これを使用して、値に基づいて各テストでイプシロンを手動で設定する必要がないようにしたいと考えています。上記の場合、1e-6 で十分です。ただし、値が 1e-10 の場合、1e-6 のイプシロンを使用しても問題が発生しない可能性があります。開発者がしなければならない選択肢が多ければ多いほど、エラーの余地が大きくなります。

私の質問は次のとおりです。この手法はposixシステムで安定しているべきですか? つまり、比較される 2 つの浮動小数点数がまったく同じ手順で生成された場合、それらの内部表現はまったく同じでなければなりません。

編集:さらに言えば、最終的には CU_ASSERT_FLOAT_EQUAL マクロが必要です。

4

5 に答える 5

6

浮動小数点値を比較するのは難しいです。文字列を混ぜても物事が改善されるわけではなく、確かにイプシロンのようにわずかな余裕が生じるわけではありません.

この記事をご覧ください: Comparing Floating Point Numbers, 2012 Edition . 私のお金では、ULPが最適です。厄介なエッジ ケースがいくつかありますが、おそらくそれらのほとんどを無視できます。

編集:5月31日 これについてもう少し考えてみると、あなたが求めているのは「テスト関数とテスト中の関数でフロートを計算するためにまったく同じ手順が使用されている場合、答えはまったく同じでしょうか? +/- いくつかの小さなエラーを心配する必要がないように?」

答えはイエスです。それらは同じです。しかし、その場合、テスト対象のコードに誤りがある場合、テスト関数にも同じ誤りが必然的に存在するため、単体テストは無意味になります。

ところで、memcmp(&x, &y, sizeof(x)) の使用に気を取られないでください。これは、両方の値にまったく同じビットが設定されていることをテストするだけです。それが真なら、x == y は必ず真になります。x == y に固執します。

于 2012-05-30T16:08:21.767 に答える
2

最初の質問で行われたように、floatをdoubleと比較することに注意してください。フロートは必然的に精度を失うため、完全精度の倍精度浮動小数点数と比較すると、計算が完全であっても、数値が等しくない場合があります。

詳細については、 http://randomascii.wordpress.com/2012/06/26/doubles-are-not-floats-so-dont-compare-them/を参照してください。

于 2012-07-01T17:20:48.460 に答える
1

したがって、答えはノーのようです。

その理由は、各変数の計算に同じ一連の計算を使用する場合でも、値を計算するステップの間にコードがあると、@ AProgrammerの応答の注記に示されているように、異なる丸め誤差が発生する可能性があるためです。

問題は、nビット浮動小数点を宣言しても、それがより大きなレジスタに格納される可能性があることです(x87は80ビットレジスタを使用します)。値がレジスタからメモリにプッシュされて他の操作のためにレジスタが解放されると、値は切り捨てられます(丸められますか?メモはどこに行きましたか...)。値がレジスタに戻されると、失われた精度が残りの計算を実行します。

一方、別のコードは、値を計算する際にまったく同じ手順を実行する場合があります。ただし、値がレジスタからプッシュされない場合(または別の場所でプッシュされる場合...)、値が再びメモリに格納されるときに、異なる切り捨てが発生します。

これはすべて、gccメーリングリスト/バグレポートのメモに従ってIEEE承認されています。

私は80386以来レジスタに触れていないので、最新のx86およびamd_64プロセッサが何を持っているかを推測することしかできません。しかし、gccのヒントなしに、x86では基本的なx87レジスタセットまたは基本的なSSEレジスタセットを使用していると推測しています。

したがって、経験則ではfabs(xy)<イプシロンを使用します。@Martin Beckettがコメントした投稿で指摘されているように、CUnitの場合はdouble形式で提供されます(そして、私が取得する習慣があるのと同じくらい肛門になりたい場合は、マクロのfloatバージョンを簡単に書くことができます)の上。

于 2012-06-06T15:27:23.057 に答える
1
float r, x;

r = atof("17.99");
CU_ASSERT(0 == memcmp(&x, &r, sizeof(float));

と同じ効果があるはずです

r = atof("17.99");
CU_ASSERT(x == r);

atofはdoubleを返すので、

CU_ASSERT(x == atof("17.99"));

違いますが

CU_ASSERT(x == (float)atof("17.99"));

同じである必要があります。

また、gccオプティマイザーには、x87から継承された命令を使用するときに、x86でこれに関する長年のバグがあったことにも注意してください(私が間違っていなければ、x86_64では発生しません)。

于 2012-05-29T14:09:56.300 に答える