2つの浮動小数点数がほぼ等しいかどうかをテストする関数を作成しました( このコードレビューの質問 を参照)。私は自分の機能を単体テストしたいのですが、これを行うための最良の方法に前向きではありません。明らかに、しきい値内で等しいはずの任意の数を選択できますが、等しいはずであるがナイーブな等式テストに失敗した(丸め誤差のため)実際の計算が等しいと見なされることをテストする方がはるかに便利なようです関数。
それは有効ですか、それとも魔法の数字を選んで先に進むべきですか?人々が歴史的に使用してきた標準的なテストケース/例はありますか?私は何かを見つけようとしましたが、わかっているのは、浮動小数点単体テストで正確な等価性を使用すべきではない理由を説明するたくさんのリファレンスだけでした。
例として、私は次のようなテストを書くことができます(gtestを使用):
template <typename FP>
class FloatEquality {
protected:
FP left, right, diff;
std::size_t ulps;
virtual void SetUp()
{
left = 2.0;
right = 2.1;
diff = .2;
}
};
TYPED_TEST_CASE_P(FloatEquality);
TYPED_TEST_P(FloatEquality, MagicNumbers)
{
EXPECT_TRUE(nearlyEqual(this->left, this->right, this->diff, this->ulps));
}
REGISTER_TYPED_TEST_CASE_P(FloatEquality, MagicNumbers);
using FloatingPointTypes= ::testing::Types<float, double>;
INSTANTIATE_TYPED_TEST_CASE_P(FloatingPoint, FloatEquality, FloatingPointTypes);
これらの数字は明らかに良い選択ではありませんが、ここで選択できる魔法の数字の種類を例示しており、すべてのボックスをチェックしてコードカバレッジを向上させることができますが、それほど意味がないようです。
私は結局これをテストしている誰かのユニットの 一例 を見つけました、しかしそれはマジックナンバーアプローチです。数字はかなり適切に選択されているように見えますが、それでも正しいことを十分にテストしていないように感じます。
失敗する実際の計算を構築しても害はありません。除算と乗算を使用して、探している種類の値を確実に生成できます。しかし、比較する興味深い値を計算したら、そのたびに再計算する意味は何ですか?計算を単体テストしますか?
考慮すべきことの1つは、0.2のような数値は浮動小数点で正確に表すことができないということです。そのようなものをしきい値として使用すると、予期しない結果が生じる可能性があります。 0.5、0.25などの数値。 0.125は浮動小数点で正確です。単体テストを考えて、しきい値自体が推定値であるこれらの状況を確認することができます。
答えは、この場合にテストされるユニットをどのように考えるかによって異なります。状況を説明する方法は、「等価チェッカー」を小さな部分に分割し、それらを別々にテストすることを検討しているように見えます(ただし、部分の1つで「浮動小数点のジレンマ」に直面します)。ユニットをパーツに分割するこの場合、計算全体の一部をテストする必要があります。
ユニットを分解したくない場合は、「マジックナンバー」と呼んでもかまいません。とにかく、ユニットテストがすべてのケースをカバーしていることを確信することはできません。常に不完全な部分があります。 「ノーマル」と「コーナーケース」のデータを選び、それに対してテストするだけです。
基本レベルの計算は、CPUメーカーによってテストされている必要があります。それらを再テストする必要はありません。
さらに、ユーザーがイプシロンをNaNまたはInfに設定した場合など、いくつかの病理学的ケースをテストする必要があります。誰かが計算を使ってイプシロンを生成するのを見ることができ、それが非正規化される可能性があるので、その場合にも対処することを確認します。適切に構成されたいくつかのマジック番号を使用するのは問題ないと思います。いつでも名前付き定数にすることができ、そうすればもはやマジックナンバーではなくなります!