// Copyright Nick Thompson, 2019 // Use, modification and distribution are subject to the // Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt // or copy at http://www.boost.org/LICENSE_1_0.txt) #ifndef BOOST_MATH_TEST_TEST_HPP #define BOOST_MATH_TEST_TEST_HPP #include #include #include #include // for std::isnan #include #include #include namespace boost { namespace math { namespace test { namespace detail { static std::atomic global_error_count{0}; static std::atomic total_ulp_distance{0}; } template bool check_mollified_close(Real expected, Real computed, Real tol, std::string const & filename, std::string const & function, int line) { using std::isnan; BOOST_ASSERT_MSG(!isnan(tol), "Tolerance cannot be a nan."); BOOST_ASSERT_MSG(!isnan(expected), "Expected value cannot be a nan."); BOOST_ASSERT_MSG(tol >= 0, "Tolerance must be non-negative."); if (isnan(computed)) { std::ios_base::fmtflags f( std::cerr.flags() ); std::cerr << std::setprecision(3); std::cerr << "\033[0;31mError at " << filename << ":" << function << ":" << line << ":\n" << " \033[0m Computed value is a nan\n"; std::cerr.flags(f); ++detail::global_error_count; return false; } using std::max; using std::abs; Real denom = (max)(abs(expected), Real(1)); Real mollified_relative_error = abs(expected - computed)/denom; if (mollified_relative_error > tol) { Real dist = abs(boost::math::float_distance(expected, computed)); detail::total_ulp_distance += static_cast(dist); std::ios_base::fmtflags f( std::cerr.flags() ); std::cerr << std::setprecision(3); std::cerr << "\033[0;31mError at " << filename << ":" << function << ":" << line << ":\n" << " \033[0m Mollified relative error in " << boost::core::demangle(typeid(Real).name())<< " precision is " << mollified_relative_error << ", which exceeds " << tol << ", error/tol = " << mollified_relative_error/tol << ".\n" << std::setprecision(std::numeric_limits::digits10) << std::showpos << " Expected: " << std::defaultfloat << std::fixed << expected << std::hexfloat << " = " << expected << "\n" << " Computed: " << std::defaultfloat << std::fixed << computed << std::hexfloat << " = " << computed << "\n" << std::defaultfloat << " ULP distance: " << dist << "\n"; std::cerr.flags(f); ++detail::global_error_count; return false; } return true; } template bool check_ulp_close(PreciseReal expected1, Real computed, size_t ulps, std::string const & filename, std::string const & function, int line) { using std::max; using std::abs; using std::isnan; // Of course integers can be expected values, and they are exact: if (!std::is_integral::value) { BOOST_ASSERT_MSG(sizeof(PreciseReal) >= sizeof(Real), "The expected number must be computed in higher (or equal) precision than the number being tested."); BOOST_ASSERT_MSG(!isnan(expected1), "Expected value cannot be a nan."); } if (isnan(computed)) { std::ios_base::fmtflags f( std::cerr.flags() ); std::cerr << std::setprecision(3); std::cerr << "\033[0;31mError at " << filename << ":" << function << ":" << line << ":\n" << " \033[0m Computed value is a nan\n"; std::cerr.flags(f); ++detail::global_error_count; return false; } Real expected = Real(expected1); Real dist = abs(boost::math::float_distance(expected, computed)); if (dist > ulps) { detail::total_ulp_distance += static_cast(dist); Real denom = (max)(abs(expected), Real(1)); Real mollified_relative_error = abs(expected - computed)/denom; std::ios_base::fmtflags f( std::cerr.flags() ); std::cerr << std::setprecision(3); std::cerr << "\033[0;31mError at " << filename << ":" << function << ":" << line << ":\n" << " \033[0m ULP distance in " << boost::core::demangle(typeid(Real).name())<< " precision is " << dist << ", which exceeds " << ulps; if (ulps > 0) { std::cerr << ", error/ulps = " << dist/static_cast(ulps) << ".\n"; } else { std::cerr << ".\n"; } std::cerr << std::setprecision(std::numeric_limits::digits10) << std::showpos << " Expected: " << std::defaultfloat << std::fixed << expected << std::hexfloat << " = " << expected << "\n" << " Computed: " << std::defaultfloat << std::fixed << computed << std::hexfloat << " = " << computed << "\n" << std::defaultfloat << " Mollified relative error: " << mollified_relative_error << "\n"; std::cerr.flags(f); ++detail::global_error_count; return false; } return true; } int report_errors() { if (detail::global_error_count > 0) { std::cerr << "\033[0;31mError count: " << detail::global_error_count; if (detail::total_ulp_distance > 0) { std::cerr << ", total ulp distance = " << detail::total_ulp_distance << "\n\033[0m"; } else { // else we overflowed the ULPs counter and all we could print is a bizarre negative number. std::cerr << "\n\033[0m"; } detail::global_error_count = 0; detail::total_ulp_distance = 0; return 1; } std::cout << "\x1B[32mNo errors detected.\n\033[0m"; return 0; } }}} #define CHECK_MOLLIFIED_CLOSE(X, Y, Z) boost::math::test::check_mollified_close< typename std::remove_reference::type>((X), (Y), (Z), __FILE__, __func__, __LINE__) #define CHECK_ULP_CLOSE(X, Y, Z) boost::math::test::check_ulp_close((X), (Y), (Z), __FILE__, __func__, __LINE__) #endif