summaryrefslogtreecommitdiffstats
path: root/src/boost/libs/math/test/math_unit_test.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/boost/libs/math/test/math_unit_test.hpp')
-rw-r--r--src/boost/libs/math/test/math_unit_test.hpp150
1 files changed, 150 insertions, 0 deletions
diff --git a/src/boost/libs/math/test/math_unit_test.hpp b/src/boost/libs/math/test/math_unit_test.hpp
new file mode 100644
index 00000000..dd664c1f
--- /dev/null
+++ b/src/boost/libs/math/test/math_unit_test.hpp
@@ -0,0 +1,150 @@
+// 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 <atomic>
+#include <iostream>
+#include <iomanip>
+#include <cmath> // for std::isnan
+#include <boost/assert.hpp>
+#include <boost/math/special_functions/next.hpp>
+#include <boost/core/demangle.hpp>
+
+namespace boost { namespace math { namespace test {
+
+namespace detail {
+ static std::atomic<int64_t> global_error_count{0};
+ static std::atomic<int64_t> total_ulp_distance{0};
+}
+
+template<class Real>
+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<int64_t>(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<Real>::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<class PreciseReal, class Real>
+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<PreciseReal>::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<int64_t>(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<Real>(ulps) << ".\n";
+ }
+ else
+ {
+ std::cerr << ".\n";
+ }
+ std::cerr << std::setprecision(std::numeric_limits<Real>::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<decltype((Y))>::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