gpt4 book ai didi

c++ - 带有 boost::multiprecision 的单元测试

转载 作者:行者123 更新时间:2023-12-02 10:13:26 34 4
gpt4 key购买 nike

自从调整了一些代码以启用多精度后,我的一些单元测试开始失败。头文件:

#ifndef SCRATCH_UNITTESTBOOST_INCLUDED
#define SCRATCH_UNITTESTBOOST_INCLUDED

#include <boost/multiprecision/cpp_dec_float.hpp>
// typedef double FLOAT;
typedef boost::multiprecision::cpp_dec_float_50 FLOAT;
const FLOAT ONE(FLOAT(1));

struct Rect
{
Rect(const FLOAT &width, const FLOAT &height) : Width(width), Height(height){};
FLOAT getArea() const { return Width * Height; }
FLOAT Width, Height;
};
#endif
主要测试文件:
#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE RectTest
#include <boost/test/unit_test.hpp>
#include "SCRATCH_UnitTestBoost.h"
namespace utf = boost::unit_test;

// Failing
BOOST_AUTO_TEST_CASE(AreaTest1)
{
Rect R(ONE / 2, ONE / 3);
FLOAT expected_area = (ONE / 2) * (ONE / 3);

std::cout << std::setprecision(std::numeric_limits<FLOAT>::digits10) << std::showpoint;
std::cout << "Expected: " << expected_area << std::endl;
std::cout << "Actual : " << R.getArea() << std::endl;

// BOOST_CHECK_EQUAL(expected_area, R.getArea());
BOOST_TEST(expected_area == R.getArea());
}

// Tolerance has no effect?
BOOST_AUTO_TEST_CASE(AreaTestTol, *utf::tolerance(1e-40))
{
Rect R(ONE / 2, ONE / 3);
FLOAT expected_area = (ONE / 2) * (ONE / 3);
BOOST_TEST(expected_area == R.getArea());
}

// Passing
BOOST_AUTO_TEST_CASE(AreaTest2)
{
Rect R(ONE / 7, ONE / 2);
FLOAT expected_area = (ONE / 7) * (ONE / 2);
BOOST_CHECK_EQUAL(expected_area, R.getArea());
}
请注意,在定义 FLOAT 时作为 double类型,所有测试通过。让我感到困惑的是,当打印确切的预期值和实际值(参见 AreaTest1)时,我们会看到相同的结果。但是 BOOST_TEST报错是:
    error: in "AreaTest1": check expected_area == R.getArea() has failed 
[0.16666666666666666666666666666666666666666666666666666666666666666666666666666666 !=
0.16666666666666666666666666666666666666666666666666666666666666666666666672236366]
使用 g++ SCRATCH_UnitTestBoost.cpp -o utb.o -lboost_unit_test_framework 编译.
问题:
  • 为什么测试失败?
  • 为什么使用toleranceAreaTestTol不按照文档提供输出 here ?

  • 相关资料:
  • Tolerances with floating point comparison
  • Gotchas with multiprecision types
  • 最佳答案

    两个问题:

  • 区别从何而来
  • 如何应用epsilon?

  • 差异从何而来
    Boost Multiprecision 使用模板表达式来推迟评估。
    此外,您正在选择一些不能以 10 为底数精确表示的有理分数(cpp_dec_float 使用十进制,因此以 10 为底数)。
    这意味着当你做
    T x = 1/3;
    T y = 1/7;
    这实际上会不精确地近似这两个分数。
    这样做:
    T z = 1/3 * 1/7;
    将实际评估右侧 表达式模板 , 所以不要像 x 这样计算临时变量回答 y之前,右手边有一个类型:

    expression<detail::multiplies, detail::expression<?>, detail::expression<?>, [2 * ...]>


    这是从实际类型缩短的:
    boost::multiprecision::detail::expression<
    boost::multiprecision::detail::multiplies,
    boost::multiprecision::detail::expression<
    boost::multiprecision::detail::divide_immediates,
    boost::multiprecision::number<boost::multiprecision::backends::cpp_dec_float<50u,
    int, void>, (boost::multiprecision::expression_template_option)1>, int,
    void, void>,
    boost::multiprecision::detail::expression<
    boost::multiprecision::detail::divide_immediates,
    boost::multiprecision::number<boost::multiprecision::backends::cpp_dec_float<50u,
    int, void>, (boost::multiprecision::expression_template_option)1>, int,
    void, void>,
    void, void>
    长话短说,这就是您想要的,因为它可以节省您的工作并保持更好的准确性,因为表达式首先被标准化为 1/(3*7)所以 1/21 .
    这就是你的差异首先来自哪里。通过以下任一方式修复它:
  • 关闭表达式模板
    using T = boost::multiprecision::number<
    boost::multiprecision::cpp_dec_float<50>,
    boost::multiprecision::et_off > >;
  • 将表达式重写为等效于您的实现:
    T expected_area = T(ONE / 7) * T(ONE / 2);
    T expected_area = (ONE / 7).eval() * (ONE / 2).eval();

  • 应用公差
    我发现很难解析 Boost Unit Test 文档,但这里有经验数据:
    BOOST_CHECK_EQUAL(expected_area, R.getArea());
    T const eps = std::numeric_limits<T>::epsilon();
    BOOST_CHECK_CLOSE(expected_area, R.getArea(), eps);
    BOOST_TEST(expected_area == R.getArea(), tt::tolerance(eps));
    这失败了第一个,并通过了最后两个。确实,除此之外,以下两个也失败了:
    BOOST_CHECK_EQUAL(expected_area, R.getArea());
    BOOST_TEST(expected_area == R.getArea());
    因此,在 utf::tolerance 之前似乎必须做一些事情。装饰器生效。使用 native double 测试告诉我只有 BOOST_TEST隐式应用公差。所以潜入了预处理扩展:
        ::boost::unit_test::unit_test_log.set_checkpoint(
    ::boost::unit_test::const_string(
    "/home/sehe/Projects/stackoverflow/test.cpp",
    sizeof("/home/sehe/Projects/stackoverflow/test.cpp") - 1),
    static_cast<std::size_t>(42));
    ::boost::test_tools::tt_detail::report_assertion(
    (::boost::test_tools::assertion::seed()->*a == b).evaluate(),
    (::boost::unit_test::lazy_ostream::instance()
    << ::boost::unit_test::const_string("a == b", sizeof("a == b") - 1)),
    ::boost::unit_test::const_string(
    "/home/sehe/Projects/stackoverflow/test.cpp",
    sizeof("/home/sehe/Projects/stackoverflow/test.cpp") - 1),
    static_cast<std::size_t>(42), ::boost::test_tools::tt_detail::CHECK,
    ::boost::test_tools::tt_detail::CHECK_BUILT_ASSERTION, 0);
    } while (::boost::test_tools::tt_detail::dummy_cond());
    深入挖掘,我遇到了:
    /*!@brief Indicates if a type can be compared using a tolerance scheme
    *
    * This is a metafunction that should evaluate to @c mpl::true_ if the type
    * @c T can be compared using a tolerance based method, typically for floating point
    * types.
    *
    * This metafunction can be specialized further to declare user types that are
    * floating point (eg. boost.multiprecision).
    */
    template <typename T>
    struct tolerance_based : tolerance_based_delegate<T, !is_array<T>::value && !is_abstract_class_or_function<T>::value>::type {};
    我们有它!但不是,
    static_assert(boost::math::fpc::tolerance_based<double>::value);
    static_assert(boost::math::fpc::tolerance_based<cpp_dec_float_50>::value);
    两者都已经通过了。唔。
    查看装饰器,我注意到注入(inject)到夹具上下文中的容差是 输入 .
    实验我得出的结论是公差装饰器 需要拥有相同的 静态类型参数 作为比较中的操作数使其生效。
    这实际上可能非常有用(您可以对不同的浮点类型有不同的隐式容差),但这也令人惊讶。
    TL;博士
    这是固定的完整测试集,供您欣赏:
  • 考虑评估顺序和对准确性的影响
  • 使用 utf::tolerance(v) 中的静态类型匹配您的操作数
  • 不要使用 BOOST_CHECK_EQUAL 进行基于容差的比较
  • 我建议使用显式 test_tools::tolerance而不是依靠“环境”容忍。毕竟,我们想要测试我们的代码,而不是测试框架

  • 住在科利鲁
    template <typename T> struct Rect {
    Rect(const T &width, const T &height) : width(width), height(height){};
    T getArea() const { return width * height; }
    private:
    T width, height;
    };

    #define BOOST_TEST_DYN_LINK
    #define BOOST_TEST_MODULE RectTest
    #include <boost/multiprecision/cpp_dec_float.hpp>
    using DecFloat = boost::multiprecision::cpp_dec_float_50;

    #include <boost/test/unit_test.hpp>
    namespace utf = boost::unit_test;
    namespace tt = boost::test_tools;

    namespace {
    template <typename T>
    static inline const T Eps = std::numeric_limits<T>::epsilon();

    template <typename T> struct Fixture {
    T const epsilon = Eps<T>;
    T const ONE = 1;
    using Rect = ::Rect<T>;

    void checkArea(int wdenom, int hdenom) const {
    auto w = ONE/wdenom; // could be expression templates
    auto h = ONE/hdenom;

    Rect const R(w, h);
    T expect = w*h;
    BOOST_TEST(expect == R.getArea(), "1/" << wdenom << " x " << "1/" << hdenom);

    // I'd prefer explicit toleranc
    BOOST_TEST(expect == R.getArea(), tt::tolerance(epsilon));
    }
    };

    }

    BOOST_AUTO_TEST_SUITE(Rectangles)
    BOOST_FIXTURE_TEST_SUITE(Double, Fixture<double>, *utf::tolerance(Eps<double>))
    BOOST_AUTO_TEST_CASE(check2_3) { checkArea(2, 3); }
    BOOST_AUTO_TEST_CASE(check7_2) { checkArea(7, 2); }
    BOOST_AUTO_TEST_CASE(check57_31) { checkArea(57, 31); }
    BOOST_AUTO_TEST_SUITE_END()
    BOOST_FIXTURE_TEST_SUITE(MultiPrecision, Fixture<DecFloat>, *utf::tolerance(Eps<DecFloat>))
    BOOST_AUTO_TEST_CASE(check2_3) { checkArea(2, 3); }
    BOOST_AUTO_TEST_CASE(check7_2) { checkArea(7, 2); }
    BOOST_AUTO_TEST_CASE(check57_31) { checkArea(57, 31); }
    BOOST_AUTO_TEST_SUITE_END()
    BOOST_AUTO_TEST_SUITE_END()
    打印
    enter image description here

    关于c++ - 带有 boost::multiprecision 的单元测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62726097/

    34 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com