]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/libs/math/test/math_unit_test.hpp
import quincy beta 17.1.0
[ceph.git] / ceph / src / boost / libs / math / test / math_unit_test.hpp
CommitLineData
92f5a8d4
TL
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>
20effc67 15#include <boost/math/special_functions/trunc.hpp>
92f5a8d4
TL
16#include <boost/core/demangle.hpp>
17
18namespace boost { namespace math { namespace test {
19
20namespace detail {
21 static std::atomic<int64_t> global_error_count{0};
22 static std::atomic<int64_t> total_ulp_distance{0};
23}
24
25template<class Real>
26bool check_mollified_close(Real expected, Real computed, Real tol, std::string const & filename, std::string const & function, int line)
27{
28 using std::isnan;
29 BOOST_ASSERT_MSG(!isnan(tol), "Tolerance cannot be a nan.");
30 BOOST_ASSERT_MSG(!isnan(expected), "Expected value cannot be a nan.");
31 BOOST_ASSERT_MSG(tol >= 0, "Tolerance must be non-negative.");
32 if (isnan(computed)) {
33 std::ios_base::fmtflags f( std::cerr.flags() );
34 std::cerr << std::setprecision(3);
35 std::cerr << "\033[0;31mError at " << filename << ":" << function << ":" << line << ":\n"
36 << " \033[0m Computed value is a nan\n";
37 std::cerr.flags(f);
38 ++detail::global_error_count;
39 return false;
40 }
41 using std::max;
42 using std::abs;
43 Real denom = (max)(abs(expected), Real(1));
44 Real mollified_relative_error = abs(expected - computed)/denom;
45 if (mollified_relative_error > tol)
46 {
47 Real dist = abs(boost::math::float_distance(expected, computed));
48 detail::total_ulp_distance += static_cast<int64_t>(dist);
49 std::ios_base::fmtflags f( std::cerr.flags() );
50 std::cerr << std::setprecision(3);
51 std::cerr << "\033[0;31mError at " << filename << ":" << function << ":" << line << ":\n"
52 << " \033[0m Mollified relative error in " << boost::core::demangle(typeid(Real).name())<< " precision is " << mollified_relative_error
53 << ", which exceeds " << tol << ", error/tol = " << mollified_relative_error/tol << ".\n"
20effc67 54 << std::setprecision(std::numeric_limits<Real>::max_digits10) << std::showpos
92f5a8d4
TL
55 << " Expected: " << std::defaultfloat << std::fixed << expected << std::hexfloat << " = " << expected << "\n"
56 << " Computed: " << std::defaultfloat << std::fixed << computed << std::hexfloat << " = " << computed << "\n"
57 << std::defaultfloat
58 << " ULP distance: " << dist << "\n";
59 std::cerr.flags(f);
60 ++detail::global_error_count;
61
62 return false;
63 }
64 return true;
65}
66
67template<class PreciseReal, class Real>
68bool check_ulp_close(PreciseReal expected1, Real computed, size_t ulps, std::string const & filename, std::string const & function, int line)
69{
70 using std::max;
71 using std::abs;
72 using std::isnan;
20effc67 73 using boost::math::lltrunc;
92f5a8d4
TL
74 // Of course integers can be expected values, and they are exact:
75 if (!std::is_integral<PreciseReal>::value) {
92f5a8d4 76 BOOST_ASSERT_MSG(!isnan(expected1), "Expected value cannot be a nan.");
20effc67
TL
77 if (sizeof(PreciseReal) < sizeof(Real)) {
78 std::ostringstream err;
79 err << "\n\tThe expected number must be computed in higher (or equal) precision than the number being tested.\n";
80 err << "\tType of expected is " << boost::core::demangle(typeid(PreciseReal).name()) << ", which occupies " << sizeof(PreciseReal) << " bytes.\n";
81 err << "\tType of computed is " << boost::core::demangle(typeid(Real).name()) << ", which occupies " << sizeof(Real) << " bytes.\n";
82 throw std::logic_error(err.str());
83 }
92f5a8d4
TL
84 }
85
86 if (isnan(computed))
87 {
88 std::ios_base::fmtflags f( std::cerr.flags() );
89 std::cerr << std::setprecision(3);
90 std::cerr << "\033[0;31mError at " << filename << ":" << function << ":" << line << ":\n"
91 << " \033[0m Computed value is a nan\n";
92 std::cerr.flags(f);
93 ++detail::global_error_count;
94 return false;
95 }
96
97 Real expected = Real(expected1);
98 Real dist = abs(boost::math::float_distance(expected, computed));
99 if (dist > ulps)
100 {
20effc67
TL
101 detail::total_ulp_distance += static_cast<int64_t>(lltrunc(dist));
102 Real abs_expected = abs(expected);
103 Real denom = (max)(abs_expected, Real(1));
92f5a8d4
TL
104 Real mollified_relative_error = abs(expected - computed)/denom;
105 std::ios_base::fmtflags f( std::cerr.flags() );
106 std::cerr << std::setprecision(3);
107 std::cerr << "\033[0;31mError at " << filename << ":" << function << ":" << line << ":\n"
108 << " \033[0m ULP distance in " << boost::core::demangle(typeid(Real).name())<< " precision is " << dist
109 << ", which exceeds " << ulps;
110 if (ulps > 0)
111 {
112 std::cerr << ", error/ulps = " << dist/static_cast<Real>(ulps) << ".\n";
113 }
114 else
115 {
116 std::cerr << ".\n";
117 }
20effc67 118 std::cerr << std::setprecision(std::numeric_limits<Real>::max_digits10) << std::showpos
92f5a8d4
TL
119 << " Expected: " << std::defaultfloat << std::fixed << expected << std::hexfloat << " = " << expected << "\n"
120 << " Computed: " << std::defaultfloat << std::fixed << computed << std::hexfloat << " = " << computed << "\n"
121 << std::defaultfloat
122 << " Mollified relative error: " << mollified_relative_error << "\n";
123 std::cerr.flags(f);
124 ++detail::global_error_count;
125 return false;
126 }
127 return true;
128}
129
20effc67
TL
130template<typename Real>
131bool check_le(Real lesser, Real greater, std::string const & filename, std::string const & function, int line)
132{
133 using std::max;
134 using std::abs;
135 using std::isnan;
136
137 if (isnan(lesser))
138 {
139 std::ios_base::fmtflags f( std::cerr.flags() );
140 std::cerr << std::setprecision(3);
141 std::cerr << "\033[0;31mError at " << filename << ":" << function << ":" << line << ":\n"
142 << " \033[0m Lesser value is a nan\n";
143 std::cerr.flags(f);
144 ++detail::global_error_count;
145 return false;
146 }
147
148 if (isnan(greater))
149 {
150 std::ios_base::fmtflags f( std::cerr.flags() );
151 std::cerr << std::setprecision(3);
152 std::cerr << "\033[0;31mError at " << filename << ":" << function << ":" << line << ":\n"
153 << " \033[0m Greater value is a nan\n";
154 std::cerr.flags(f);
155 ++detail::global_error_count;
156 return false;
157 }
158
159 if (lesser > greater)
160 {
161 std::ios_base::fmtflags f( std::cerr.flags() );
162 std::cerr << std::setprecision(3);
163 std::cerr << "\033[0;31mError at " << filename << ":" << function << ":" << line << ":\n"
164 << " \033[0m Condition " << lesser << " \u2264 " << greater << " is violated in " << boost::core::demangle(typeid(Real).name()) << " precision.\n";
165 std::cerr << std::setprecision(std::numeric_limits<Real>::max_digits10) << std::showpos
166 << " \"Lesser\" : " << std::defaultfloat << std::fixed << lesser << " = " << std::scientific << lesser << std::hexfloat << " = " << lesser << "\n"
167 << " \"Greater\": " << std::defaultfloat << std::fixed << greater << " = " << std::scientific << greater << std::hexfloat << " = " << greater << "\n"
168 << std::defaultfloat;
169 std::cerr.flags(f);
170 ++detail::global_error_count;
171 return false;
172 }
173 return true;
174}
175
176
177template<class PreciseReal, class Real>
178bool check_conditioned_error(Real abscissa, PreciseReal expected1, PreciseReal expected_derivative, Real computed, Real acceptable_badness, std::string const & filename, std::string const & function, int line)
179{
180 using std::max;
181 using std::abs;
182 using std::isnan;
183 // Of course integers can be expected values, and they are exact:
184 if (!std::is_integral<PreciseReal>::value) {
185 BOOST_ASSERT_MSG(sizeof(PreciseReal) >= sizeof(Real),
186 "The expected number must be computed in higher (or equal) precision than the number being tested.");
187 BOOST_ASSERT_MSG(!isnan(abscissa), "Expected abscissa cannot be a nan.");
188 BOOST_ASSERT_MSG(!isnan(expected1), "Expected value cannot be a nan.");
189 BOOST_ASSERT_MSG(!isnan(expected_derivative), "Expected derivative cannot be a nan.");
190 }
191 BOOST_ASSERT_MSG(acceptable_badness >= 1, "Acceptable badness scale must be >= 1, and in general should = 1 exactly.");
192
193 if (isnan(computed))
194 {
195 std::ios_base::fmtflags f(std::cerr.flags());
196 std::cerr << std::setprecision(3);
197 std::cerr << "\033[0;31mError at " << filename << ":" << function << ":" << line << ":\n"
198 << " \033[0m Computed value is a nan\n";
199 std::cerr.flags(f);
200 ++detail::global_error_count;
201 return false;
202 }
203
204 Real mu = std::numeric_limits<Real>::epsilon()/2;
205 Real expected = Real(expected1);
206 // Relative error is undefined. Therefore we must use |f(x(1+eps))| le mu|xf'(x)|.
207 if (expected == 0)
208 {
209 Real tol = acceptable_badness*mu*abs(abscissa*expected_derivative);
210 if (abs(computed) > tol)
211 {
212 std::ios_base::fmtflags f( std::cerr.flags() );
213 std::cerr << std::setprecision(3);
214 std::cerr << "\033[0;31mError at " << filename << ":" << function << ":" << line << ":\n";
215 std::cerr << std::setprecision(std::numeric_limits<Real>::max_digits10) << std::showpos;
216 std::cerr << "\033[0m Error at abscissa " << std::defaultfloat << std::fixed << abscissa << " = " << std::hexfloat << abscissa << "\n";
217 std::cerr << " Given that the expected value is zero, the computed value in " << boost::core::demangle(typeid(Real).name()) << " precision must satisfy |f(x)| <= " << tol << ".\n";
218 std::cerr << " But the computed value is " << std::defaultfloat << std::fixed << computed << std::hexfloat << " = " << computed << "\n";
219 std::cerr.flags(f);
220 ++detail::global_error_count;
221 return false;
222 }
223 }
224 // 1 ULP accuracy * acceptable_badness is always acceptable, independent of condition number:
225 if (abs(boost::math::float_distance(Real(expected), computed)) <= acceptable_badness)
226 {
227 return true;
228 }
229 Real expected_prime = Real(expected_derivative);
230 PreciseReal precise_abscissa = abscissa;
231 PreciseReal cond = abs(precise_abscissa*expected_prime/expected);
232 PreciseReal relative_error = abs((expected - PreciseReal(computed))/expected);
233 // If the condition number is small, then we revert to allowing 1ULP accuracy, i.e., one incorrect bit.
234 Real tol = cond*mu;
235 tol *= acceptable_badness;
236 if (relative_error > tol)
237 {
238 std::ios_base::fmtflags f( std::cerr.flags() );
239 std::cerr << std::setprecision(3);
240 std::cerr << "\033[0;31mError at " << filename << ":" << function << ":" << line << "\n";
241 std::cerr << std::setprecision(std::numeric_limits<Real>::max_digits10);
242 std::cerr << "\033[0m The relative error at abscissa x = " << std::defaultfloat << std::fixed << abscissa << " = " << std::hexfloat << abscissa
243 << " in " << boost::core::demangle(typeid(Real).name()) << " precision is " << std::scientific << relative_error << "\n"
244 << " This exceeds the tolerance " << tol << "\n"
245 << std::showpos
246 << " Expected: " << std::defaultfloat << std::fixed << expected << " = " << std::scientific << expected << std::hexfloat << " = " << expected << "\n"
247 << " Computed: " << std::defaultfloat << std::fixed << computed << " = " << std::scientific << computed << std::hexfloat << " = " << computed << "\n"
248 << " Condition number of function evaluation: " << std::noshowpos << std::defaultfloat << std::scientific << cond << " = " << std::fixed << cond << "\n"
249 << " Badness scale required to make this message go away: " << std::defaultfloat << relative_error/(cond*mu) << "\n";
250 std::cerr.flags(f);
251 ++detail::global_error_count;
252 return false;
253 }
254 return true;
255}
256
257
258template<class PreciseReal, class Real>
259bool check_absolute_error(PreciseReal expected1, Real computed, Real acceptable_error, std::string const & filename, std::string const & function, int line)
260{
261 using std::max;
262 using std::abs;
263 using std::isnan;
264 // Of course integers can be expected values, and they are exact:
265 if (!std::is_integral<PreciseReal>::value) {
266 BOOST_ASSERT_MSG(sizeof(PreciseReal) >= sizeof(Real),
267 "The expected number must be computed in higher (or equal) precision than the number being tested.");
268 BOOST_ASSERT_MSG(!isnan(expected1), "Expected value cannot be a nan (use CHECK_NAN if this is your intention).");
269 }
270 BOOST_ASSERT_MSG(acceptable_error > 0, "Error must be > 0.");
271
272 if (isnan(computed))
273 {
274 std::ios_base::fmtflags f(std::cerr.flags());
275 std::cerr << std::setprecision(3);
276 std::cerr << "\033[0;31mError at " << filename << ":" << function << ":" << line << ":\n"
277 << " \033[0m Computed value is a nan\n";
278 std::cerr.flags(f);
279 ++detail::global_error_count;
280 return false;
281 }
282
283 Real expected = Real(expected1);
284 Real error = abs(expected - computed);
285 if (error > acceptable_error)
286 {
287 std::ios_base::fmtflags f( std::cerr.flags() );
288 std::cerr << std::setprecision(3);
289 std::cerr << "\033[0;31mError at " << filename << ":" << function << ":" << line << "\n";
290 std::cerr << std::setprecision(std::numeric_limits<Real>::max_digits10);
291 std::cerr << "\033[0m The absolute error in " << boost::core::demangle(typeid(Real).name()) << " precision is " << std::scientific << error << "\n"
292 << " This exceeds the acceptable error " << acceptable_error << "\n"
293 << std::showpos
294 << " Expected: " << std::defaultfloat << std::fixed << expected << " = " << std::scientific << expected << std::hexfloat << " = " << expected << "\n"
295 << " Computed: " << std::defaultfloat << std::fixed << computed << " = " << std::scientific << computed<< std::hexfloat << " = " << computed << "\n"
296 << " Error/Acceptable error: " << std::defaultfloat << error/acceptable_error << "\n";
297 std::cerr.flags(f);
298 ++detail::global_error_count;
299 return false;
300 }
301 return true;
302}
303
304template<class Real>
305bool check_nan(Real x, std::string const & filename, std::string const & function, int line)
306{
307 using std::isnan;
308 if (!isnan(x)) {
309 std::ios_base::fmtflags f( std::cerr.flags() );
310 std::cerr << "\033[0;31mError at " << filename << ":" << function << ":" << line << "\n";
311 std::cerr << "\033[0m The computed value should be a nan, but is instead " << std::defaultfloat << std::fixed << x << " = " << std::scientific << x << std::hexfloat << " = " << x << "\n";
312 std::cerr.flags(f);
313 ++detail::global_error_count;
314 return false;
315 }
316 return true;
317}
318
319template<class Real>
320bool check_equal(Real x, Real y, std::string const & filename, std::string const & function, int line)
321{
322 using std::isnan;
323 if (x != y) {
324 std::ios_base::fmtflags f( std::cerr.flags() );
325 std::cerr << "\033[0;31mError at " << filename << ":" << function << ":" << line << "\n";
326 std::cerr << "\033[0m Condition '" << x << " == " << y << "' is not satisfied:\n";
327 if (std::is_floating_point<Real>::value) {
328 std::cerr << " Expected = " << std::defaultfloat << std::fixed << x << " = " << std::scientific << x << std::hexfloat << " = " << x << "\n";
329 std::cerr << " Computed = " << std::defaultfloat << std::fixed << y << " = " << std::scientific << y << std::hexfloat << " = " << y << "\n";
330 } else {
331 std::cerr << " Expected: " << x << " = " << "0x" << std::hex << x << "\n";
332 std::cerr << std::dec;
333 std::cerr << " Computed: " << y << " = " << "0x" << std::hex << y << "\n";
334 }
335 std::cerr.flags(f);
336 ++detail::global_error_count;
337 return false;
338 }
339 return true;
340}
341
92f5a8d4
TL
342
343int report_errors()
344{
345 if (detail::global_error_count > 0)
346 {
347 std::cerr << "\033[0;31mError count: " << detail::global_error_count;
348 if (detail::total_ulp_distance > 0) {
349 std::cerr << ", total ulp distance = " << detail::total_ulp_distance << "\n\033[0m";
350 }
351 else {
352 // else we overflowed the ULPs counter and all we could print is a bizarre negative number.
353 std::cerr << "\n\033[0m";
354 }
355
356 detail::global_error_count = 0;
357 detail::total_ulp_distance = 0;
358 return 1;
359 }
360 std::cout << "\x1B[32mNo errors detected.\n\033[0m";
361 return 0;
362}
363
364}}}
365
366#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__)
367
368#define CHECK_ULP_CLOSE(X, Y, Z) boost::math::test::check_ulp_close((X), (Y), (Z), __FILE__, __func__, __LINE__)
369
20effc67
TL
370#define CHECK_LE(X, Y) boost::math::test::check_le((X), (Y), __FILE__, __func__, __LINE__)
371
372#define CHECK_NAN(X) boost::math::test::check_nan((X), __FILE__, __func__, __LINE__)
373
374#define CHECK_EQUAL(X, Y) boost::math::test::check_equal((X), (Y), __FILE__, __func__, __LINE__)
375
376#define CHECK_CONDITIONED_ERROR(V, W, X, Y, Z) boost::math::test::check_conditioned_error((V), (W), (X), (Y), (Z), __FILE__, __func__, __LINE__)
377
378#define CHECK_ABSOLUTE_ERROR(X, Y, Z) boost::math::test::check_absolute_error((X), (Y), (Z), __FILE__, __func__, __LINE__)
379
92f5a8d4 380#endif