math_unit_test.hpp 6.2 KB

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