• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright Nick Thompson, 2019
2 // Use, modification and distribution are subject to the
3 // Boost Software License, Version 1.0.
4 // (See accompanying file LICENSE_1_0.txt
5 // or copy at http://www.boost.org/LICENSE_1_0.txt)
6 
7 #ifndef BOOST_MATH_TEST_TEST_HPP
8 #define BOOST_MATH_TEST_TEST_HPP
9 #include <atomic>
10 #include <iostream>
11 #include <iomanip>
12 #include <cmath> // for std::isnan
13 #include <boost/assert.hpp>
14 #include <boost/math/special_functions/next.hpp>
15 #include <boost/core/demangle.hpp>
16 
17 namespace boost { namespace math { namespace  test {
18 
19 namespace detail {
20     static std::atomic<int64_t> global_error_count{0};
21     static std::atomic<int64_t> total_ulp_distance{0};
22 }
23 
24 template<class Real>
check_mollified_close(Real expected,Real computed,Real tol,std::string const & filename,std::string const & function,int line)25 bool check_mollified_close(Real expected, Real computed, Real tol, std::string const & filename, std::string const & function, int line)
26 {
27     using std::isnan;
28     BOOST_ASSERT_MSG(!isnan(tol), "Tolerance cannot be a nan.");
29     BOOST_ASSERT_MSG(!isnan(expected), "Expected value cannot be a nan.");
30     BOOST_ASSERT_MSG(tol >= 0, "Tolerance must be non-negative.");
31     if (isnan(computed)) {
32         std::ios_base::fmtflags f( std::cerr.flags() );
33         std::cerr << std::setprecision(3);
34         std::cerr << "\033[0;31mError at " << filename << ":" << function << ":" << line << ":\n"
35                   << " \033[0m Computed value is a nan\n";
36         std::cerr.flags(f);
37         ++detail::global_error_count;
38         return false;
39     }
40     using std::max;
41     using std::abs;
42     Real denom = (max)(abs(expected), Real(1));
43     Real mollified_relative_error = abs(expected - computed)/denom;
44     if (mollified_relative_error > tol)
45     {
46         Real dist = abs(boost::math::float_distance(expected, computed));
47         detail::total_ulp_distance += static_cast<int64_t>(dist);
48         std::ios_base::fmtflags f( std::cerr.flags() );
49         std::cerr << std::setprecision(3);
50         std::cerr << "\033[0;31mError at " << filename << ":" << function << ":" << line << ":\n"
51                   << " \033[0m Mollified relative error in " << boost::core::demangle(typeid(Real).name())<< " precision is " << mollified_relative_error
52                   << ", which exceeds " << tol << ", error/tol  = " << mollified_relative_error/tol << ".\n"
53                   << std::setprecision(std::numeric_limits<Real>::digits10) << std::showpos
54                   << "  Expected: " << std::defaultfloat << std::fixed << expected << std::hexfloat << " = " << expected << "\n"
55                   << "  Computed: " << std::defaultfloat << std::fixed << computed << std::hexfloat << " = " << computed << "\n"
56                   << std::defaultfloat
57                   << "  ULP distance: " << dist << "\n";
58         std::cerr.flags(f);
59         ++detail::global_error_count;
60 
61         return false;
62     }
63     return true;
64 }
65 
66 template<class PreciseReal, class Real>
check_ulp_close(PreciseReal expected1,Real computed,size_t ulps,std::string const & filename,std::string const & function,int line)67 bool check_ulp_close(PreciseReal expected1, Real computed, size_t ulps, std::string const & filename, std::string const & function, int line)
68 {
69     using std::max;
70     using std::abs;
71     using std::isnan;
72     // Of course integers can be expected values, and they are exact:
73     if (!std::is_integral<PreciseReal>::value) {
74         BOOST_ASSERT_MSG(sizeof(PreciseReal) >= sizeof(Real),
75                          "The expected number must be computed in higher (or equal) precision than the number being tested.");
76         BOOST_ASSERT_MSG(!isnan(expected1), "Expected value cannot be a nan.");
77     }
78 
79     if (isnan(computed))
80     {
81         std::ios_base::fmtflags f( std::cerr.flags() );
82         std::cerr << std::setprecision(3);
83         std::cerr << "\033[0;31mError at " << filename << ":" << function << ":" << line << ":\n"
84                   << " \033[0m Computed value is a nan\n";
85         std::cerr.flags(f);
86         ++detail::global_error_count;
87         return false;
88     }
89 
90     Real expected = Real(expected1);
91     Real dist = abs(boost::math::float_distance(expected, computed));
92     if (dist > ulps)
93     {
94         detail::total_ulp_distance += static_cast<int64_t>(dist);
95         Real denom = (max)(abs(expected), Real(1));
96         Real mollified_relative_error = abs(expected - computed)/denom;
97         std::ios_base::fmtflags f( std::cerr.flags() );
98         std::cerr << std::setprecision(3);
99         std::cerr << "\033[0;31mError at " << filename << ":" << function << ":" << line << ":\n"
100                   << " \033[0m ULP distance in " << boost::core::demangle(typeid(Real).name())<< " precision is " << dist
101                   << ", which exceeds " << ulps;
102                   if (ulps > 0)
103                   {
104                       std::cerr << ", error/ulps  = " << dist/static_cast<Real>(ulps) << ".\n";
105                   }
106                   else
107                   {
108                       std::cerr << ".\n";
109                   }
110         std::cerr << std::setprecision(std::numeric_limits<Real>::digits10) << std::showpos
111                   << "  Expected: " << std::defaultfloat << std::fixed << expected << std::hexfloat << " = " << expected << "\n"
112                   << "  Computed: " << std::defaultfloat << std::fixed << computed << std::hexfloat << " = " << computed << "\n"
113                   << std::defaultfloat
114                   << "  Mollified relative error: " << mollified_relative_error << "\n";
115         std::cerr.flags(f);
116         ++detail::global_error_count;
117         return false;
118     }
119     return true;
120 }
121 
122 
report_errors()123 int report_errors()
124 {
125     if (detail::global_error_count > 0)
126     {
127         std::cerr << "\033[0;31mError count: " << detail::global_error_count;
128         if (detail::total_ulp_distance > 0) {
129             std::cerr << ", total ulp distance = " << detail::total_ulp_distance << "\n\033[0m";
130         }
131         else {
132             // else we overflowed the ULPs counter and all we could print is a bizarre negative number.
133             std::cerr << "\n\033[0m";
134         }
135 
136         detail::global_error_count = 0;
137         detail::total_ulp_distance = 0;
138         return 1;
139     }
140     std::cout << "\x1B[32mNo errors detected.\n\033[0m";
141     return 0;
142 }
143 
144 }}}
145 
146 #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__)
147 
148 #define CHECK_ULP_CLOSE(X, Y, Z) boost::math::test::check_ulp_close((X), (Y), (Z), __FILE__, __func__, __LINE__)
149 
150 #endif
151