Browse Source

Added equal and notEqual overload with max ULPs parameters for scalar numbers #121

Christophe Riccio 7 years ago
parent
commit
72327cea4f
4 changed files with 118 additions and 6 deletions
  1. 22 0
      glm/ext/scalar_relational.hpp
  2. 42 1
      glm/ext/scalar_relational.inl
  3. 3 0
      readme.md
  4. 51 5
      test/ext/ext_scalar_relational.cpp

+ 22 - 0
glm/ext/scalar_relational.hpp

@@ -37,6 +37,28 @@ namespace glm
 	template<typename genType>
 	GLM_FUNC_DECL GLM_CONSTEXPR bool notEqual(genType const& x, genType const& y, genType const& epsilon);
 
+	/// Returns the component-wise comparison between two scalars in term of ULPs.
+	/// True if this expression is satisfied.
+	///
+	/// @param x First operand.
+	/// @param y Second operand.
+	/// @param ULPs Maximum difference in ULPs between the two operators to consider them equal.
+	///
+	/// @tparam genType Floating-point or integer scalar types
+	template<typename genType>
+	GLM_FUNC_DECL GLM_CONSTEXPR bool equal(genType const& x, genType const& y, int ULPs);
+
+	/// Returns the component-wise comparison between two scalars in term of ULPs.
+	/// True if this expression is not satisfied.
+	///
+	/// @param x First operand.
+	/// @param y Second operand.
+	/// @param ULPs Maximum difference in ULPs between the two operators to consider them not equal.
+	///
+	/// @tparam genType Floating-point or integer scalar types
+	template<typename genType>
+	GLM_FUNC_DECL GLM_CONSTEXPR bool notEqual(genType const& x, genType const& y, int ULPs);
+
 	/// @}
 }//namespace glm
 

+ 42 - 1
glm/ext/scalar_relational.inl

@@ -1,7 +1,24 @@
 #include "../common.hpp"
+#include "../ext/scalar_int_sized.hpp"
+#include "../ext/scalar_uint_sized.hpp"
 
-namespace glm
+namespace glm{
+namespace detail
 {
+	// https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
+	union float_t
+	{
+		GLM_CONSTEXPR float_t(float Num = 0.0f) : f(Num) {}
+		// Portable extraction of components.
+		GLM_CONSTEXPR bool negative() const { return i < 0; }
+		GLM_CONSTEXPR int32 mantissa() const { return i & ((1 << 23) - 1); }
+		GLM_CONSTEXPR int32 exponent() const { return (i >> 23) & 0xFF; }
+
+		int32 const i;
+		float const f;
+	};
+}//namespace detail
+
 	template<typename genType>
 	GLM_FUNC_QUALIFIER GLM_CONSTEXPR bool equal(genType const& x, genType const& y, genType const& epsilon)
 	{
@@ -13,4 +30,28 @@ namespace glm
 	{
 		return abs(x - y) > epsilon;
 	}
+
+	template<typename genType>
+	GLM_FUNC_QUALIFIER GLM_CONSTEXPR bool equal(genType const& x, genType const& y, int MaxULPs)
+	{
+		detail::float_t const a(x);
+		detail::float_t const b(y);
+
+		// Different signs means they do not match.
+		if(a.negative() != b.negative())
+		{
+			// Check for equality to make sure +0==-0
+			return a.mantissa() == b.mantissa() && a.exponent() == b.exponent();
+		}
+
+		// Find the difference in ULPs.
+		int const DiffULPs = abs(a.i - b.i);
+		return DiffULPs <= MaxULPs;
+	}
+
+	template<typename genType>
+	GLM_FUNC_QUALIFIER GLM_CONSTEXPR bool notEqual(genType const& x, genType const& y, int ULPs)
+	{
+		return !equal(x, y, ULPs);
+	}
 }//namespace glm

+ 3 - 0
readme.md

@@ -53,6 +53,9 @@ glm::mat4 camera(float Translate, glm::vec2 const& Rotate)
 ## Release notes
 
 ### [GLM 0.9.9.2](https://github.com/g-truc/glm/commits/master) - 2018-XX-XX
+#### Features:
+- Added equal and notEqual overload with max ULPs parameters for scalar numbers #121
+
 #### Fixes:
 - Fixed GLM_FORCE_CXX** section in the manual
 

+ 51 - 5
test/ext/ext_scalar_relational.cpp

@@ -1,6 +1,7 @@
 #include <glm/ext/scalar_relational.hpp>
+#include <cmath>
 
-int test_equal()
+int test_equal_epsilon()
 {
 #	if GLM_CONFIG_CONSTEXP == GLM_ENABLE
 		static_assert(glm::equal(1.01f, 1.02f, 0.1f), "GLM: Failed constexpr");
@@ -15,7 +16,7 @@ int test_equal()
 	return Error;
 }
 
-int test_notEqual()
+int test_notEqual_epsilon()
 {
 #	if GLM_CONFIG_CONSTEXP == GLM_ENABLE
 		static_assert(glm::notEqual(1.01f, 1.02f, 0.001f), "GLM: Failed constexpr");
@@ -30,12 +31,57 @@ int test_notEqual()
 	return Error;
 }
 
-int main()
+#if GLM_LANG & GLM_LANG_CXX11_FLAG
+int test_equal_ulps()
 {
 	int Error = 0;
+	
+	float const ULP1Plus = std::nextafter(1.0f, 2.0f);
+	Error += glm::equal(1.0f, ULP1Plus, 1) ? 0 : 1;
+
+	float const ULP2Plus = std::nextafter(ULP1Plus, 2.0f);
+	Error += !glm::equal(1.0f, ULP2Plus, 1) ? 0 : 1;
+	
+	float const ULP1Minus = std::nextafter(1.0f, 0.0f);
+	Error += glm::equal(1.0f, ULP1Minus, 1) ? 0 : 1;
 
-	Error += test_equal();
-	Error += test_notEqual();
+	float const ULP2Minus = std::nextafter(ULP1Minus, 0.0f);
+	Error += !glm::equal(1.0f, ULP2Minus, 1) ? 0 : 1;
+	
+	return Error;
+}
 
+int test_notEqual_ulps()
+{
+	int Error = 0;
+	
+	float const ULP1Plus = std::nextafter(1.0f, 2.0f);
+	Error += !glm::notEqual(1.0f, ULP1Plus, 1) ? 0 : 1;
+	
+	float const ULP2Plus = std::nextafter(ULP1Plus, 2.0f);
+	Error += glm::notEqual(1.0f, ULP2Plus, 1) ? 0 : 1;
+	
+	float const ULP1Minus = std::nextafter(1.0f, 0.0f);
+	Error += !glm::notEqual(1.0f, ULP1Minus, 1) ? 0 : 1;
+	
+	float const ULP2Minus = std::nextafter(ULP1Minus, 0.0f);
+	Error += glm::notEqual(1.0f, ULP2Minus, 1) ? 0 : 1;
+	
+	return Error;
+}
+#endif
+
+int main()
+{
+	int Error = 0;
+
+	Error += test_equal_epsilon();
+	Error += test_notEqual_epsilon();
+	
+#if GLM_LANG & GLM_LANG_CXX11_FLAG
+	Error += test_equal_ulps();
+	Error += test_notEqual_ulps();
+#endif
+	
 	return Error;
 }