catch_matchers_floating.cpp 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. /*
  2. * Created by Martin on 07/11/2017.
  3. *
  4. * Distributed under the Boost Software License, Version 1.0. (See accompanying
  5. * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  6. */
  7. #include "catch_matchers_floating.h"
  8. #include "catch_enforce.h"
  9. #include "catch_polyfills.hpp"
  10. #include "catch_to_string.hpp"
  11. #include "catch_tostring.h"
  12. #include <algorithm>
  13. #include <cmath>
  14. #include <cstdlib>
  15. #include <cstdint>
  16. #include <cstring>
  17. #include <sstream>
  18. #include <type_traits>
  19. #include <iomanip>
  20. #include <limits>
  21. namespace Catch {
  22. namespace {
  23. int32_t convert(float f) {
  24. static_assert(sizeof(float) == sizeof(int32_t), "Important ULP matcher assumption violated");
  25. int32_t i;
  26. std::memcpy(&i, &f, sizeof(f));
  27. return i;
  28. }
  29. int64_t convert(double d) {
  30. static_assert(sizeof(double) == sizeof(int64_t), "Important ULP matcher assumption violated");
  31. int64_t i;
  32. std::memcpy(&i, &d, sizeof(d));
  33. return i;
  34. }
  35. template <typename FP>
  36. bool almostEqualUlps(FP lhs, FP rhs, uint64_t maxUlpDiff) {
  37. // Comparison with NaN should always be false.
  38. // This way we can rule it out before getting into the ugly details
  39. if (Catch::isnan(lhs) || Catch::isnan(rhs)) {
  40. return false;
  41. }
  42. auto lc = convert(lhs);
  43. auto rc = convert(rhs);
  44. if ((lc < 0) != (rc < 0)) {
  45. // Potentially we can have +0 and -0
  46. return lhs == rhs;
  47. }
  48. // static cast as a workaround for IBM XLC
  49. auto ulpDiff = std::abs(static_cast<FP>(lc - rc));
  50. return static_cast<uint64_t>(ulpDiff) <= maxUlpDiff;
  51. }
  52. #if defined(CATCH_CONFIG_GLOBAL_NEXTAFTER)
  53. float nextafter(float x, float y) {
  54. return ::nextafterf(x, y);
  55. }
  56. double nextafter(double x, double y) {
  57. return ::nextafter(x, y);
  58. }
  59. #endif // ^^^ CATCH_CONFIG_GLOBAL_NEXTAFTER ^^^
  60. template <typename FP>
  61. FP step(FP start, FP direction, uint64_t steps) {
  62. for (uint64_t i = 0; i < steps; ++i) {
  63. #if defined(CATCH_CONFIG_GLOBAL_NEXTAFTER)
  64. start = Catch::nextafter(start, direction);
  65. #else
  66. start = std::nextafter(start, direction);
  67. #endif
  68. }
  69. return start;
  70. }
  71. // Performs equivalent check of std::fabs(lhs - rhs) <= margin
  72. // But without the subtraction to allow for INFINITY in comparison
  73. bool marginComparison(double lhs, double rhs, double margin) {
  74. return (lhs + margin >= rhs) && (rhs + margin >= lhs);
  75. }
  76. template <typename FloatingPoint>
  77. void write(std::ostream& out, FloatingPoint num) {
  78. out << std::scientific
  79. << std::setprecision(std::numeric_limits<FloatingPoint>::max_digits10 - 1)
  80. << num;
  81. }
  82. } // end anonymous namespace
  83. namespace Matchers {
  84. namespace Floating {
  85. enum class FloatingPointKind : uint8_t {
  86. Float,
  87. Double
  88. };
  89. WithinAbsMatcher::WithinAbsMatcher(double target, double margin)
  90. :m_target{ target }, m_margin{ margin } {
  91. CATCH_ENFORCE(margin >= 0, "Invalid margin: " << margin << '.'
  92. << " Margin has to be non-negative.");
  93. }
  94. // Performs equivalent check of std::fabs(lhs - rhs) <= margin
  95. // But without the subtraction to allow for INFINITY in comparison
  96. bool WithinAbsMatcher::match(double const& matchee) const {
  97. return (matchee + m_margin >= m_target) && (m_target + m_margin >= matchee);
  98. }
  99. std::string WithinAbsMatcher::describe() const {
  100. return "is within " + ::Catch::Detail::stringify(m_margin) + " of " + ::Catch::Detail::stringify(m_target);
  101. }
  102. WithinUlpsMatcher::WithinUlpsMatcher(double target, uint64_t ulps, FloatingPointKind baseType)
  103. :m_target{ target }, m_ulps{ ulps }, m_type{ baseType } {
  104. CATCH_ENFORCE(m_type == FloatingPointKind::Double
  105. || m_ulps < (std::numeric_limits<uint32_t>::max)(),
  106. "Provided ULP is impossibly large for a float comparison.");
  107. }
  108. #if defined(__clang__)
  109. #pragma clang diagnostic push
  110. // Clang <3.5 reports on the default branch in the switch below
  111. #pragma clang diagnostic ignored "-Wunreachable-code"
  112. #endif
  113. bool WithinUlpsMatcher::match(double const& matchee) const {
  114. switch (m_type) {
  115. case FloatingPointKind::Float:
  116. return almostEqualUlps<float>(static_cast<float>(matchee), static_cast<float>(m_target), m_ulps);
  117. case FloatingPointKind::Double:
  118. return almostEqualUlps<double>(matchee, m_target, m_ulps);
  119. default:
  120. CATCH_INTERNAL_ERROR( "Unknown FloatingPointKind value" );
  121. }
  122. }
  123. #if defined(__clang__)
  124. #pragma clang diagnostic pop
  125. #endif
  126. std::string WithinUlpsMatcher::describe() const {
  127. std::stringstream ret;
  128. ret << "is within " << m_ulps << " ULPs of ";
  129. if (m_type == FloatingPointKind::Float) {
  130. write(ret, static_cast<float>(m_target));
  131. ret << 'f';
  132. } else {
  133. write(ret, m_target);
  134. }
  135. ret << " ([";
  136. if (m_type == FloatingPointKind::Double) {
  137. write(ret, step(m_target, static_cast<double>(-INFINITY), m_ulps));
  138. ret << ", ";
  139. write(ret, step(m_target, static_cast<double>( INFINITY), m_ulps));
  140. } else {
  141. // We have to cast INFINITY to float because of MinGW, see #1782
  142. write(ret, step(static_cast<float>(m_target), static_cast<float>(-INFINITY), m_ulps));
  143. ret << ", ";
  144. write(ret, step(static_cast<float>(m_target), static_cast<float>( INFINITY), m_ulps));
  145. }
  146. ret << "])";
  147. return ret.str();
  148. }
  149. WithinRelMatcher::WithinRelMatcher(double target, double epsilon):
  150. m_target(target),
  151. m_epsilon(epsilon){
  152. CATCH_ENFORCE(m_epsilon >= 0., "Relative comparison with epsilon < 0 does not make sense.");
  153. CATCH_ENFORCE(m_epsilon < 1., "Relative comparison with epsilon >= 1 does not make sense.");
  154. }
  155. bool WithinRelMatcher::match(double const& matchee) const {
  156. const auto relMargin = m_epsilon * (std::max)(std::fabs(matchee), std::fabs(m_target));
  157. return marginComparison(matchee, m_target,
  158. std::isinf(relMargin)? 0 : relMargin);
  159. }
  160. std::string WithinRelMatcher::describe() const {
  161. Catch::ReusableStringStream sstr;
  162. sstr << "and " << m_target << " are within " << m_epsilon * 100. << "% of each other";
  163. return sstr.str();
  164. }
  165. }// namespace Floating
  166. Floating::WithinUlpsMatcher WithinULP(double target, uint64_t maxUlpDiff) {
  167. return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Double);
  168. }
  169. Floating::WithinUlpsMatcher WithinULP(float target, uint64_t maxUlpDiff) {
  170. return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Float);
  171. }
  172. Floating::WithinAbsMatcher WithinAbs(double target, double margin) {
  173. return Floating::WithinAbsMatcher(target, margin);
  174. }
  175. Floating::WithinRelMatcher WithinRel(double target, double eps) {
  176. return Floating::WithinRelMatcher(target, eps);
  177. }
  178. Floating::WithinRelMatcher WithinRel(double target) {
  179. return Floating::WithinRelMatcher(target, std::numeric_limits<double>::epsilon() * 100);
  180. }
  181. Floating::WithinRelMatcher WithinRel(float target, float eps) {
  182. return Floating::WithinRelMatcher(target, eps);
  183. }
  184. Floating::WithinRelMatcher WithinRel(float target) {
  185. return Floating::WithinRelMatcher(target, std::numeric_limits<float>::epsilon() * 100);
  186. }
  187. } // namespace Matchers
  188. } // namespace Catch