| 
									
										
										
										
											2017-11-10 18:14:42 +01:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  *  Created by Martin on 07/11/2017. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Distributed under 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)
 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "catch_matchers_floating.h"
 | 
					
						
							| 
									
										
										
										
											2018-09-03 10:03:47 +02:00
										 |  |  | #include "catch_enforce.h"
 | 
					
						
							| 
									
										
										
										
											2018-11-17 20:52:18 +01:00
										 |  |  | #include "catch_polyfills.hpp"
 | 
					
						
							| 
									
										
										
										
											2018-05-09 20:16:27 +02:00
										 |  |  | #include "catch_to_string.hpp"
 | 
					
						
							| 
									
										
										
										
											2017-11-10 21:43:23 +01:00
										 |  |  | #include "catch_tostring.h"
 | 
					
						
							| 
									
										
										
										
											2017-11-10 18:14:42 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-06 13:08:44 +02:00
										 |  |  | #include <cmath>
 | 
					
						
							| 
									
										
										
										
											2017-11-10 21:43:23 +01:00
										 |  |  | #include <cstdlib>
 | 
					
						
							| 
									
										
										
										
											2017-11-10 18:14:42 +01:00
										 |  |  | #include <cstdint>
 | 
					
						
							|  |  |  | #include <cstring>
 | 
					
						
							| 
									
										
										
										
											2019-06-14 20:16:12 +02:00
										 |  |  | #include <sstream>
 | 
					
						
							| 
									
										
										
										
											2019-09-06 13:08:44 +02:00
										 |  |  | #include <type_traits>
 | 
					
						
							| 
									
										
										
										
											2019-06-14 20:16:12 +02:00
										 |  |  | #include <iomanip>
 | 
					
						
							|  |  |  | #include <limits>
 | 
					
						
							| 
									
										
										
										
											2017-11-10 18:14:42 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-06 13:08:44 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-10 18:14:42 +01:00
										 |  |  | namespace Catch { | 
					
						
							|  |  |  | namespace Matchers { | 
					
						
							|  |  |  | namespace Floating { | 
					
						
							|  |  |  | enum class FloatingPointKind : uint8_t { | 
					
						
							|  |  |  |     Float, | 
					
						
							|  |  |  |     Double | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace { | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-05 13:23:14 +02:00
										 |  |  | int32_t convert(float f) { | 
					
						
							| 
									
										
										
										
											2017-11-10 18:14:42 +01:00
										 |  |  |     static_assert(sizeof(float) == sizeof(int32_t), "Important ULP matcher assumption violated"); | 
					
						
							|  |  |  |     int32_t i; | 
					
						
							| 
									
										
										
										
											2019-10-05 13:23:14 +02:00
										 |  |  |     std::memcpy(&i, &f, sizeof(f)); | 
					
						
							|  |  |  |     return i; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2017-11-10 18:14:42 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-05 13:23:14 +02:00
										 |  |  | int64_t convert(double d) { | 
					
						
							| 
									
										
										
										
											2017-11-10 18:14:42 +01:00
										 |  |  |     static_assert(sizeof(double) == sizeof(int64_t), "Important ULP matcher assumption violated"); | 
					
						
							|  |  |  |     int64_t i; | 
					
						
							| 
									
										
										
										
											2019-10-05 13:23:14 +02:00
										 |  |  |     std::memcpy(&i, &d, sizeof(d)); | 
					
						
							|  |  |  |     return i; | 
					
						
							| 
									
										
										
										
											2017-11-10 18:14:42 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | template <typename FP> | 
					
						
							| 
									
										
										
										
											2019-10-05 13:23:14 +02:00
										 |  |  | bool almostEqualUlps(FP lhs, FP rhs, uint64_t maxUlpDiff) { | 
					
						
							| 
									
										
										
										
											2017-11-10 18:14:42 +01:00
										 |  |  |     // Comparison with NaN should always be false.
 | 
					
						
							|  |  |  |     // This way we can rule it out before getting into the ugly details
 | 
					
						
							| 
									
										
										
										
											2018-11-17 20:52:18 +01:00
										 |  |  |     if (Catch::isnan(lhs) || Catch::isnan(rhs)) { | 
					
						
							| 
									
										
										
										
											2017-11-10 18:14:42 +01:00
										 |  |  |         return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     auto lc = convert(lhs); | 
					
						
							|  |  |  |     auto rc = convert(rhs); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-05 13:23:14 +02:00
										 |  |  |     if ((lc < 0) != (rc < 0)) { | 
					
						
							| 
									
										
										
										
											2017-11-10 18:14:42 +01:00
										 |  |  |         // Potentially we can have +0 and -0
 | 
					
						
							|  |  |  |         return lhs == rhs; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-05 13:23:14 +02:00
										 |  |  |     auto ulpDiff = std::abs(lc - rc); | 
					
						
							|  |  |  |     return static_cast<uint64_t>(ulpDiff) <= maxUlpDiff; | 
					
						
							| 
									
										
										
										
											2017-11-10 18:14:42 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-06 13:08:44 +02:00
										 |  |  | #if defined(CATCH_CONFIG_GLOBAL_NEXTAFTER)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace Catch { | 
					
						
							|  |  |  |     float nextafter(float x, float y) { | 
					
						
							|  |  |  |         return ::nextafterf(x, y); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     double nextafter(double x, double y) { | 
					
						
							|  |  |  |         return ::nextafter(x, y); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     long double nextafter(long double x, long double y) { | 
					
						
							|  |  |  |         return ::nextafterl(x, y); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } // end namespace Catch
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-14 20:16:12 +02:00
										 |  |  | template <typename FP> | 
					
						
							| 
									
										
										
										
											2019-10-05 13:23:14 +02:00
										 |  |  | FP step(FP start, FP direction, uint64_t steps) { | 
					
						
							|  |  |  |     for (uint64_t i = 0; i < steps; ++i) { | 
					
						
							| 
									
										
										
										
											2019-09-06 13:08:44 +02:00
										 |  |  | #if defined(CATCH_CONFIG_GLOBAL_NEXTAFTER)
 | 
					
						
							|  |  |  |         start = Catch::nextafter(start, direction); | 
					
						
							|  |  |  | #else
 | 
					
						
							| 
									
										
										
										
											2019-06-14 20:16:12 +02:00
										 |  |  |         start = std::nextafter(start, direction); | 
					
						
							| 
									
										
										
										
											2019-09-06 13:08:44 +02:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2019-06-14 20:16:12 +02:00
										 |  |  |     } | 
					
						
							|  |  |  |     return start; | 
					
						
							| 
									
										
										
										
											2017-11-10 18:14:42 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-14 20:16:12 +02:00
										 |  |  | } // end anonymous namespace
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-10 18:14:42 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | namespace Catch { | 
					
						
							|  |  |  | namespace Matchers { | 
					
						
							|  |  |  | namespace Floating { | 
					
						
							|  |  |  |     WithinAbsMatcher::WithinAbsMatcher(double target, double margin) | 
					
						
							| 
									
										
										
										
											2017-12-06 15:37:13 +01:00
										 |  |  |         :m_target{ target }, m_margin{ margin } { | 
					
						
							| 
									
										
										
										
											2018-09-03 10:03:47 +02:00
										 |  |  |         CATCH_ENFORCE(margin >= 0, "Invalid margin: " << margin << '.' | 
					
						
							|  |  |  |             << " Margin has to be non-negative."); | 
					
						
							| 
									
										
										
										
											2017-12-06 15:37:13 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2017-11-10 18:14:42 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // Performs equivalent check of std::fabs(lhs - rhs) <= margin
 | 
					
						
							|  |  |  |     // But without the subtraction to allow for INFINITY in comparison
 | 
					
						
							|  |  |  |     bool WithinAbsMatcher::match(double const& matchee) const { | 
					
						
							| 
									
										
										
										
											2018-03-19 20:36:07 +01:00
										 |  |  |         return (matchee + m_margin >= m_target) && (m_target + m_margin >= matchee); | 
					
						
							| 
									
										
										
										
											2017-11-10 18:14:42 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     std::string WithinAbsMatcher::describe() const { | 
					
						
							| 
									
										
										
										
											2017-11-10 21:43:23 +01:00
										 |  |  |         return "is within " + ::Catch::Detail::stringify(m_margin) + " of " + ::Catch::Detail::stringify(m_target); | 
					
						
							| 
									
										
										
										
											2017-11-10 18:14:42 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-05 13:23:14 +02:00
										 |  |  |     WithinUlpsMatcher::WithinUlpsMatcher(double target, uint64_t ulps, FloatingPointKind baseType) | 
					
						
							| 
									
										
										
										
											2017-11-10 18:14:42 +01:00
										 |  |  |         :m_target{ target }, m_ulps{ ulps }, m_type{ baseType } { | 
					
						
							| 
									
										
										
										
											2019-10-05 13:23:14 +02:00
										 |  |  |         CATCH_ENFORCE(m_type == FloatingPointKind::Double | 
					
						
							|  |  |  |                    || m_ulps < std::numeric_limits<uint32_t>::max(), | 
					
						
							|  |  |  |             "Provided ULP is impossibly large for a float comparison."); | 
					
						
							| 
									
										
										
										
											2017-11-10 18:14:42 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-01 22:34:29 +02:00
										 |  |  | #if defined(__clang__)
 | 
					
						
							|  |  |  | #pragma clang diagnostic push
 | 
					
						
							|  |  |  | // Clang <3.5 reports on the default branch in the switch below
 | 
					
						
							|  |  |  | #pragma clang diagnostic ignored "-Wunreachable-code"
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-10 18:14:42 +01:00
										 |  |  |     bool WithinUlpsMatcher::match(double const& matchee) const { | 
					
						
							|  |  |  |         switch (m_type) { | 
					
						
							|  |  |  |         case FloatingPointKind::Float: | 
					
						
							|  |  |  |             return almostEqualUlps<float>(static_cast<float>(matchee), static_cast<float>(m_target), m_ulps); | 
					
						
							|  |  |  |         case FloatingPointKind::Double: | 
					
						
							|  |  |  |             return almostEqualUlps<double>(matchee, m_target, m_ulps); | 
					
						
							|  |  |  |         default: | 
					
						
							| 
									
										
										
										
											2018-09-03 10:03:47 +02:00
										 |  |  |             CATCH_INTERNAL_ERROR( "Unknown FloatingPointKind value" ); | 
					
						
							| 
									
										
										
										
											2017-11-10 18:14:42 +01:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-01 22:34:29 +02:00
										 |  |  | #if defined(__clang__)
 | 
					
						
							|  |  |  | #pragma clang diagnostic pop
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-10 18:14:42 +01:00
										 |  |  |     std::string WithinUlpsMatcher::describe() const { | 
					
						
							| 
									
										
										
										
											2019-06-14 20:16:12 +02:00
										 |  |  |         std::stringstream ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         ret << "is within " << m_ulps << " ULPs of " << ::Catch::Detail::stringify(m_target); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (m_type == FloatingPointKind::Float) { | 
					
						
							|  |  |  |             ret << 'f'; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         ret << " (["; | 
					
						
							|  |  |  |         ret << std::fixed << std::setprecision(std::numeric_limits<double>::max_digits10); | 
					
						
							|  |  |  |         if (m_type == FloatingPointKind::Double) { | 
					
						
							|  |  |  |             ret << step(m_target, static_cast<double>(-INFINITY), m_ulps) | 
					
						
							|  |  |  |                 << ", " | 
					
						
							|  |  |  |                 << step(m_target, static_cast<double>(INFINITY), m_ulps); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             ret << step<float>(static_cast<float>(m_target), -INFINITY, m_ulps) | 
					
						
							|  |  |  |                 << ", " | 
					
						
							|  |  |  |                 << step<float>(static_cast<float>(m_target), INFINITY, m_ulps); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         ret << "])"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return ret.str(); | 
					
						
							|  |  |  |         //return "is within " + Catch::to_string(m_ulps) + " ULPs of " + ::Catch::Detail::stringify(m_target) + ((m_type == FloatingPointKind::Float)? "f" : "");
 | 
					
						
							| 
									
										
										
										
											2017-11-10 18:14:42 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | }// namespace Floating
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-05 13:23:14 +02:00
										 |  |  | Floating::WithinUlpsMatcher WithinULP(double target, uint64_t maxUlpDiff) { | 
					
						
							| 
									
										
										
										
											2017-11-10 18:14:42 +01:00
										 |  |  |     return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Double); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-05 13:23:14 +02:00
										 |  |  | Floating::WithinUlpsMatcher WithinULP(float target, uint64_t maxUlpDiff) { | 
					
						
							| 
									
										
										
										
											2017-11-10 18:14:42 +01:00
										 |  |  |     return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Float); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Floating::WithinAbsMatcher WithinAbs(double target, double margin) { | 
					
						
							|  |  |  |     return Floating::WithinAbsMatcher(target, margin); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } // namespace Matchers
 | 
					
						
							|  |  |  | } // namespace Catch
 | 
					
						
							|  |  |  | 
 |