Boost.Test浮動小数点の比較
Boost.Testで使える浮動小数点比較用マクロの紹介
問題
アプリケーションコード内では浮動小数点 (double
やfloat
)を比較することはないと思いますが、テストコードでは期待値と比較することが必要です。ただし、浮動小数点の場合、精度の問題から微小の誤差による不一致は避けられません
例えば、以下のテストコードは0.15 + 0.15 == 0.1 + 0.2
ですので、一見問題なさそうですが、私の環境では不一致となります
#include <boost/test/included/unit_test.hpp>
using namespace boost::test_tools;
template <class T>
T dut_add(const T& a, const T& b) {
return a + b;
}
BOOST_AUTO_TEST_CASE(double_comparison) {
double a = dut_add(0.15, 0.15);
double b = dut_add(0.10, 0.20);
BOOST_TEST(a == b); // !エラー
}
% clang++ -Wall -g -O2 cpp/boost_test_float.cc -o boost_test_float
% ./boost_test_float
Running 1 test case...
cpp/boost_test_float.cc(12): error: in "double_comparison": check a == b has failed [0.29999999999999999 != 0.30000000000000004]
かといって、
BOOST_TEST(abs(a - b) < 1E-10); // Passするが汎用性がない
のような書き方は汎用性がありません。なぜならどの程度の誤差が許容できるかは比較する数字の大きさに依存するからです。例えばaやbの期待値が1E-15だった場合、上記のような1E-10の誤差は大きすぎます。
Boost.Testでの書き方
このような問題に対して、Boost.Testではboost::unit_test::tolerance
で許容値を割合で指定することができます。(ドキュメント)
a = 10.0;
b = 10.2;
BOOST_TEST(a == b, tolerance(0.05)); // PASS (5%の誤差を許容)
BOOST_TEST(a == b, tolerance(0.01)); // ERROR (1%の誤差を許容)
上記の例ではaとbの誤差は2%です。最初のBOOST_TEST
はtolerance(0.05)
、すなわち5%の誤差を許容しているのでパスします。これに対して2番目の比較は1%の誤差しか許容していないため、エラーとなります。
テスト全体にまとめて指定
先ほどはBOOST_TEST()
毎に許容誤差を指定していましたが、BOOST_AUTO_TEST_CASE()
マクロの第二引数で指定することでテストケース全体にまとめて指定することも可能です
BOOST_AUTO_TEST_CASE(double_comparison_with_tolerance, * boost::unit_test::tolerance(1E-15)) {
double a = dut_add(0.15, 0.15);
double b = dut_add(0.10, 0.20);
BOOST_TEST(a == b);
}
データ型毎の許容誤差
上記のboost::unit_test::tolerance(1E-15)
はdouble
型の比較に対してのみ適用されます。float
型に対して許容誤差を設定する場合は、
BOOST_AUTO_TEST_CASE(float_comparison_with_tolerance, * boost::unit_test::tolerance(1E-6f)) {
float a = 0.15f + 0.15f;
float b = 0.01f + 0.29f;
BOOST_TEST(a == b);
}
tolerance(1E-6f)
とtolerance()
にfloat
型を設定します。その他にもlong double
やユーザ定義型に対するしても同様です
最近のコメント